Tony Pigram
Slides for presentations of things that I like talking about
All content in these presentations is for example learning exercises, it has no official support from IBM and if you wish to use this material, you must make sure it is fit for your purpose. No warranty is implied or given.
There are times when you have a web app where you need to upload images from the end user to your server. For instance, an avatar image for a user profile.
Sounds simple enough, this has been solved for decades hasn't it?
Well, yes and no.
If you're uploading to a PaaS server (IBM Bluemix, Google AppEngine, Amazon) each time you upload you code or your app crashes, it wipes out the files on the server. Ah, not so good.
Also when you start up more instances the local file system is not replicated.
This guy described the problem in relation to ghost.js
I also want to use an angularJS controller to send the image rather than a traditional HTML form POST.
Setup a NodeJS / Express / AngularJS web app on IBM Bluemix.
This web app will handle the user interaction for uploading the image.
Setup another NodeJS / Busboy / Nano app that will receive the image, save it locally and then save that image into a CloudantDB document.
The aim is to return the URL reference to the image so that this can be saved and referenced in the web application.
You could use a simple HTML5 input control to allow the user to select a file to upload and use the enctype 'multipart/form-data' to upload the file.
The /v1/upload function uses Busboy to save the file to the local storage system.
I found that if you specified an incorrect file location, the file would be saved to the /tmp folder and given a random name (not what was expected)
I was originally using the same NodeJS web app for handling ExpressJS(v3) REST API calls, using the bodyParser.
When I attempted to use Busboy at the same time, it failed to work.
If I commented out the app.use(express.bodyParser()) Busboy would then work and would save the uploaded image file.
From what I've read, if you use ExpressJS v4, bodyParser() is no longer included, it's an extra module to import. I'm not sure of the impact of this, will look into it when/if I have to upgrade.
My workaround was to create a specific web app that just handles the image upload using Busboy and saving to CloudantDB.
CTRL
The HTML file contains our custom Directive called 'file-model'.
When we press the 'upload me' button, we call the CTRL code.
This calls the 'fileUpload' Service that performs a $http call to '/v1/upload'. This saves the file.
We then call 'getURL' to get the 'http' URL
that we need to use.
HTML
SERVICE
DIRECTIVE
BUSBOY
/app/public/images/
In this specific NodeJS web app, we
load ExpressJS and Busboy.
For logging, I was trying out the module called 'ibmbluemix'.
The res.header overrides allow us to call this REST API from the other web app (yes, it does mean it's less secure and can be improved upon, but this is a demo).
The .createWriteStream() open the file to save the image to, when it has finished we return the location of the file back to the caller.
This is more for success/failure purposes.
BUSBOY
/app/public/images/
cloudantDB
Again, we use the res.header overrides to allow calling this REST API from our web application.
We determine the contentType (used for saving later).
We use .readFileSync() to read the temporary stored file.
We construct the cloudantImageUrl to pass back to the web application.
We use nano to connect to the 'images' CloudantDB
We create an intial document, get the 'id' and 'rev' values, we then perform an .attachment.insert() to add the image to the document.
We create an intial document, as shown here.
We then get the 'id' and 'rev' values to allow the image to be attached to the correct document.
We then perform an .attachment.insert() to add the image to the document.
CloudantDB then converts this to an _attachment{} element of the original document, as shown here.
In a web browser, we select 'Choose File', this opens an Explorer window to allow us to select which file to upload. I selected '1301897.jpg' and pressed the 'upload me' button.
This then returned in the console.log the output 'filename returned=1301897.jpg'
If we check in IBM Bluemix 'Files and Logs' we can see that file in the '/app/public/images' folder. (Remember, these files will disappear if you upload a new version of your app via 'cf push' or perform an 'instance restart')
Then pressing the 'get URL' button,
returns the URL that we are looking for.
By Tony Pigram
How to upload images to a PaaS environment from an AngularJS web application, store the image into a CloudantDB document and obtain a URL reference to the uploaded image. (This is a small part of my larger ACE! 'Conference/Event' mobile app)