A few days back I posted about how Parse's JavaScript API now makes it easy to upload files via their SDK. The demo I built was very quick and simple, and while it made use of PhoneGap, it wasn't a great example of the technologies together. Before I spoke on Parse at PhoneGap Day (apparently videos will be posted soon, I'll share when they are) I whipped up a slightly nicer example. Let's take a look.
My example is (I'm sorry) another example of a Note taking app. However this time I've added the ability to attach pictures to a note. The home screen is a listing of your current notes, sorted by date.

Clicking the plus symbol takes you to a form allowing you to write a new note.

At this point you can select to take a picture. Now - for testing purposes in my iOS Simulator, I set the source to the local file system. In a real world app you would ask for the camera itself (or allow the user to select), but I wanted something quick and dirty.
Once you click Save, we then create a new Note object at Parse. The code has to determine if you've taken a picture or not and if you have, it will handle the upload for you. Now let's look at the code.
First - the home page. I'm using jQuery Mobile for the application and have placed both "pages" in the core index.html. Since there seems to be some confusion about this, let me be absolutely clear. jQuery Mobile does not make you use one html page. Period. In a case like this where I have a small app (2 pages), then it made sense for me to include them in one html file. That was 100% a personal choice and not anything jQuery Mobile forced me to do.
The HTML here is pretty bare since almost all of the content is dynamic. Now let's take a look at app.js.
First take a look at the pageshow event for #home. This is where we get data from Parse. This is done via a simple query that orders by object creation. I limit the count to 10 and if I wanted to could add paging.
The addNote logic is a bit more complex. Saving Parse data is asynchronous so if we need to store a file we have two, not one, async calls to make. Hence the big IF block that checks if we've got an existing selected image. To be honest this could be done a bit nicer perhaps. For example, the initial creation of the Note object could definitely be taken out of the IF clause, as well as the line where I set the text property. But in general I think you get the idea.
Anyway, I hope this is useful for folks. I've zipped up a copy of this application and attached it to the blog entry.
Archived Comments
hi
i downloaded and testing on android
but does not work for me
have you any idea?
Nope, you have to tell me *where* it is failing.
Hi Ray, nice post. Have you tried FatFractal? We put up a blog post, inspired by yours, demonstrating how to do this with our platform:
http://fatfractal.com/prod/...
If you haven't already, check us out!
Interesting. I like the fact you can do both stores at once. Parse does allow for N storage calls at once, but not for a 'related' thing as we have here.
I am getting "Uncaught TypeError : undefined is not a function:181" when setting parseFile = new Parse.File("mypic.jpg", {base64:imagedata});
Any ideas?
Also, how would I go about adding the option to get the image from the camera instead of the gallery?
Sorry,I use .getPicture to get it from the camera.
Ensure you have the latest release of Parse's JavaScript file.
I am still getting the "Uncaught TypeError: undefined is not a function when setting parseFile = new Parse.File("mypic.jpg", {base64: imagedata});
Here is a pastebin: http://pastebin.com/AxmLECbU
Any help would be greatly appreciated.
It appears that it was the javascript file. Thanks!
thanks for share.
i downloaded and test in android but it not work for me.
what is the variable "parseJSID" ?
expect your replay.
Parse JS ID comes from *your* Parse settings. Please see the Parse docs, or my earlier posts, if that doesn't make sense.
Hey Raymond, great article - One of the problems I frequently run into though with phonegap is uploading photos with the base64 data causes the app to crash from memory issues, do you know how you could upload just using the file?
Well, you have a FileTransfer API in PhoneGap that works perfectly for binary. But you can't use that with the Parse Upload API. You *could* obviously use it as a 'mix' - so you upload the file to some S3 location, or some other server, get the URL, and use the URL in the Parse object.
You may also want to try different values for Quality as well as Target Height/Width. By default those mobile pics are freaking huge and you probably don't need them that big.
Hi,
I tried it out, and it worked perfectly, thanks! At the beginning I tried to do what the phonegap.docs recommend, by using uploadPhoto, instead of gotPic. Im sorry for all the code below.
navigator.camera.getPicture(uploadPhoto,function(message) { alert('get picture failed'); },{ quality: 50, destinationType: navigator.camera.DestinationType.FILE_URI,sourceType: navigator.camera.PictureSourceType.PHOTOLIBRARY }
And then get base64 by using FileReader:
function readDataUrl(file) {
var reader = new FileReader();
reader.onloadend = function(evt) {
base = evt.target.result;
};
reader.readAsDataURL(file);
}
However, this gave me a base64 string, but an empty picture. I get the same problem with mediafiles, and wondering if you could help me out. I get a base64 string, but when I upload it to parse, the audio file is empty. I want to get a file, by using its name, like I do below:
function gotFS(fileSystem) {
fileSystem.root.getFile('rec.mp3', {create: false}, gotFileEntry, fail);
}
function gotFileEntry(fileEntry) {
fileEntry.file(gotFile, fail);
}
function gotFile(file){
readDataUrl(file);
}
function readDataUrl(file) {
var reader = new FileReader();
reader.onloadend = function(evt) {
base = evt.target.result;
};
reader.readAsDataURL(file);
}
Hmm. I'd remove Parse from the picture. Try adding the base64 string to the DOM as an image. I believe you would do that by setting the SRC equal to data:image/png;base64,X where X is the base64 data.
Like this?
var file = new Parse.File("sound.x", {base64:base}, "image/png");
If so, it didn't work. I believe I get the base64 of an empty file, because that happened with the image I tried to upload when I used the readDataUrl to get the base64 string. Is any other methods I can use to get the file's base64 than the methods above? To start the gotFile method above, Im using window.requestFileSystem(LocalFileSystem.PERSISTENT, 100*1024, gotFS, fail);
No, what I meant was - you are having an issue with the base64 becoming a 'real' file on Parse. My suggestion was to NOT upload it (for now) and test it by writing the string out into the DOM for testing purposes.
Hi,
I'm struggling to get the file upload to work on ios and android when Im using cordova. Something strange with Audio recording... Im building an application using cordova, and using the media object to record the audio for both Android and iOS. I record the media file as a .wav file (recording.wav) for both platforms. When Im finished with the recording, I retrieve the base64 encoded string with the file system and readDataUrl (base= evt.target.result). The base64 encoded string looks good for both devices, its just one big difference and that is ios have a much larger base64encoded string than Android.
I play the recorded media to be sure that everything is good, which it is and upload the base64 encoded string to parse.com as a file. Before I upload it, I remove the first letters in the base64 which is on Android: data:audio/x-wav;base64, and on iOS: data:audio/wav;base64, . When I try to play the sound I recorded on android, on a android device it works perfectly. The same for iphone, When I try to play the sound I recorded on Iphone on a android device, every thing works fine. However, when I try to play the sound I recorded on the android phone on a iphone, it does not work!
I try to play the media file in parse.com, and on iOS it works fine, but on Android it looks empty.. and I cannot play it from the web, just on a android phone using my application and retrieving the url from parse.com.
I play the media file just by retrieving the url from the file object from parse.com. I guess this is something due to the fact that android record the wav file on a different way? If I look on the base64 from the wav file in android, it is as I mentioned much smaller, and complete random characters. However, on iOS its a LOT of "A"s in the beginning of the base64 string. Am I suppose to add lots of A's to the base64 before I send it up as a file to parse.com?
Please help if you have any knowledge of this.
Wow, sorry, you got me on this. Maybe post it to the Parse support forum?
I did, still no answer. I just got one quick question if you know the answer to it. I have now altered the media plugin to change the bitrate and output format from default to this
this.recorder.setAudioSamplingRate(44100);
this.recorder.setAudioEncodingBitRate(96000);
this.recorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
this.recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
I read a place that this was necessary to do, to be able to play it on ios. I have tried the extensions m4v, m4a, mp4 and mp3 but none of them work on the ios.
On ios I get the error "Failed to initialize AVAudioPlayer: The operation couldn’t be completed. (OSStatus error 2003334207.)" and then the error code 4 which means that the audio is not supported. Any idea why this might be?
Sorry, no, I don't.
Let us say I want to take two picture note, how can I display it in a grid?
Display what? The pictures?
Hi Raymon, thanks for your quick respond.
I would like to show the picture in a grid view as an attachment, so later in the process all the attached files could be saved.
Like
AttachButton
Grid view of Attached Files
Attachement 1 ...... ... Picture1
Attachement 2 ...... ... Picture2
Attachement 3 ...... ... Audio file
Thanks
At a high level, the code would need to remember a list of selected files and render them in a list.
Um... I guess that's not terribly helpful. But you get the idea. So I'd begin by modifying my code to allow for multiple clicks. My imagedata variable would need to become an array that you append to.
Mr Camden,
I've been thinking of the best way to implement something like this for a while and am curious on your thoughts. My question: when using these cloud storage services, what is the best way to identify by the phone and not by user to easily retrieve data from the db. I don't need my users to login I just want them to get the data associated with their previous app sessions. So far I've thought maybe the best way is to use the phone UUID as the "key".
The issue with that is - I believe - iOS prevents you from getting the phone UUID. Actually I'm wrong - from the docs:
The uuid on iOS is not unique to a device, but varies for each application, for each installation. It changes if you delete and re-install the app, and possibly also when you upgrade iOS, or even upgrade the app per version (apparent in iOS 5.1). The uuid is not a reliable value.
I think the crucial part though is that is "not reliable." Check out this post from Parse:
https://parse.com/questions...
From what I see there - it sounds like you can use an anonymous user to get a unique ID.
Hi Mr Camden,
I am using cordova for file upload and recording in ios. I recorded one audio and very next movement i am try to upload that audio and it successfully uploaded.But i record one audio and this file automatically saved in "voice memo" in ios and after few hours later i want to upload that audio, i cant fetch the file from voice memo n ios. The directory shows only for photo and video files not for audio.how to fetch voice memo files for upload?? please help me to figure out.
Sorry, not sure about that one. I'd recommend posting a bit more details about the issue to Stack Overflow.
How to get text files, or pdf files from phone. It only works for images?
Well it depends. My example uses base64. You could use Cordova's File API to read in a file and convert it to base64.
Looking at the JS API now for this, https://parse.com/docs/js/s..., it looks as if you could pass a FIle object, which should be possible w/ Cordova. So give it a shot.
Please please send me files please, the link is broken... really i need testing
fanstein@hotmail.com
Link is fixed.
How to make MySQL?
How to make what?