Drupal Services File Create Example with JSON and JQuery

Category: 

Note, please read about this issue with the Services module before getting started!

UPDATE: There is security issue with the Services module prior to version 3.4. Now all autheticated service calls that use POST, PUT or DELETE need a CSRF token sent along in the request header. This article has NOT been udpated to show those changes! More Information and Workaround(s)

Here is some example javascript that can be used to create a new file by using the File Create resource from the Services module in Drupal.

In this example, we'll be taking a base 64 encoded image string in javascript, deciding its file name and path to save in Drupal.

var data = {
  "file":{
    "file":my_base_64_encoded_image_string,
    "filename":"my_image.jpg",
    "filepath":"sites/default/files/my_image.jpg", /* in D7 change this value to "public://my_image.jpg" */
  }
};

Next we'll build our ajax options and make the call to our services endpoint and corresponding file create resource...

options = {
  type:"post",
  data:data,
  url: Drupal.settings.basePath + 'my_services_endpoint/file.json',
  dataType: 'json',
  success:function(result){
    console.log(JSON.stringify(result));
  }
};
$.ajax(options);

When it is successful, and we will be, the 'result' variable will contain the new Drupal file id and uri.

{
  "fid":123,
  "uri":"http://www.example.com/my_services_endpoint/file/123"
}

Now get out there and make me proud, create some image files in Drupal gosh darn it!

Note, if you're not using jQuery, and just using a JS XHR, then you must JSON.stringify(data) before sending it!

Comments

I cannot change the filepath to any subfolder such as sites/default/files/images/. all files r listed under the files folder. Can you give me some help? Thanks a lot.

tyler's picture

I would imagine if you change the "filepath" value in the "data" variable in javascript to the directory of your choice, that should work. I haven't tried to save it anywhere but the sites/default/files directory. Also, I would check to make sure the directory you are trying to write to exists and has the proper write permissions.

Hi Tyler,

I am using rest services in drupal 7 to upload images from iOs to drupal site. I am facing issue when trying to upload image larger than 800kB (content length of request is nearly 1 000 000 characters).

I have already posted question on http://drupal.org/node/1862478 .

Can you please take look at it and give your thoghts.

Thanks,

Milorad

tyler's picture

Hi Milorad,

I'm sorry, I haven't run into this issue. But I'm sure it is real. I've always thought what is the limitation for the string length. In the past, I've just compressed the image quality down before sending it along so the string isn't so huge. I'll have to defer to the Services module team for specifics though.

What do you mean "my_base_64_encoded_image_string" ؟ 

thx :)

tyler's picture

Hi Bilhip,

Basically, it is a giant string that represents an image, and that's what the File Create Service Resource needs. There are techniques to get a base 64 encoded string of an image in javascript, hope this google search helps.

Hi tyler .. i used poster in firefox ... but don't work 

 

http://localhost/d/?q=my_services/file.json    

 application/json

data = {  "file":{    "file":"data:image/jpeg;base64,/9j/4AAQSkeuWBs .........",    "filename":"my_image.jpg",    "filepath":"public://my_image.jpg",  }}; 

tyler's picture

Here are some suggestions:

  • check your services module permissions and make sure the user is allowed to create files
  • check your destination directory permissions
  • make sure your Services module is up to date
  • review the error code response from POSTER and google it
  • check out the Services module issue queue

Tyler, thanks for all your hard work on Drupalgap.  I've been working with it to set up an app over the past week and although I've hit some speed bumps, the Drupal Services part of it has been one of the easiest parts because of your tutorials.  

I'm trying to capture photos to associate with a node now, and I'm wondering if you had any thoughts on how to properly do this?  Would you put an upload photo button on the node create screen much like Drupal's desktop interface, then associate the returned URL with the node?  Run everything on submit and make the user wait on the file uploading, then the node create?    

tyler's picture

For other projects, I've put the 'add photo' button on the node edit screen of the app. Then used the File Create resource to create the file, then use the Node Update resource to attach the new file id to the node's image field. They key is associating just the file id with the field on your node.

Soon (soon-ish), DrupalGap will support filefield and images, so this will be pretty easy to accomplish. It still needs some more fine tuning, but if you check out the 7.x-1.x-dev version of the mobile app development kit on Github, that will shed some light on it.

Hey Tyler -- how exactly would we attach a file to a node? Would the same procedure apply to attaching an image to a node or a user, should that field be exposed properly?

tyler's picture

Hi Cameron,

Please see my reply to Aaron above, it should cover most of the points.

I've had this request so many times, I really want to write a blog post about just this topic. One of these days...

Just what I was looking. Thanks for the example!!

Hi, may i know how did you get your my_base_64_encoded_image_string?

tyler's picture

Hi Nicole,

For my particular use case, I was using PhoneGap's camera feature, which automatically returns a base 64 encoded string to me. I would need to know more about your use case to help, hopefully this will point you in the right direction: https://www.google.com/search?q=how+to+base64+encode+an+image

Hello, 

"my_base_64_encoded_image_string" means you have to pass the base64 encoded string as the image file. 

Reference: http://stackoverflow.com/a/11246772

var data = {
  "file":{
    "file": encodeImageUri(image_path),
    "filename":"my_image.jpg",
    "filepath":"sites/default/files/my_image.jpg", /* in D7 change this value to "public://my_image.jpg" */
  }
};

 

Hope This Helps!

//San.

 

Hi tyler,

     Thanks for writing detailed articles. All these are very helpful . 

In my case am able to upload image to files after uploading i need to add that image to my content type. There in webservice what i need to specify

Please help me out.

 

Thanks in advance.

tyler's picture

You will use the Node Create and/or Update service resource. Once you have the file id from the File Create resource, you can then pass that file id along with your Content Type's field.

For example, this would be the data string to send to the Node Create resource to create a new Article node and attach the new image to the article's image field:

var data = "type=article&title=ImageTest&field_image[0][fid]=" + file_id;

 

hi, I'm really stuck with this; I'm uploading an image file and getting the fid but the subsequent request to create a node and attach the fid fails. The node is create although the image field is not filled in with the image fid and the image is not attached to the node. 

I use this ajax to post the node:

$.ajax({

        url: localStorage.appurl+"/api/node.json",

        type: 'post',

       data: "node[title]=new node&node[type]=ftritem&node[field_ftritem_images][und][0][fid]=7895",

        dataType: 'json',

        headers: {

          'X-CSRF-Token': localStorage.usertoken

        },

        error: function(XMLHttpRequest, textStatus, errorThrown) {

          console.log('error '+errorThrown);


        },

        success: function (data) {         

        console.log("Node created");


        }

      }); 

 

Im getting this error:

Notice: Undefined offset: 0 in image_field_widget_form() (line 358 of /var/www/dt11/modules/image/image.field.inc).

Notice: Undefined offset: 0 in file_field_widget_form() (line 526 of /var/www/dt11/modules/file/file.field.inc).

Has anyone faced this before ? Any suggestions are highly appreaciated.

tyler's picture

Try removing the 'und' language code from the file field data string.

Also, try creating a node with an image on your site, then use Firefox Poster and GET on /my_endpoint/node/123.json, then copy/paste the JSON into jsonlint.com, examine the exact structure that is expected for the image field, then build your data query string to match it.

Hi tyler

Thanks for your quick response. When am trying to upload image

Am getting following callback error.. I culdnt able to get the sollution please help out.

I/Web Console(18020): Error in success callback: Capture2 = ReferenceError: cannot find the variable at file:///android_asset/www/cordova-1.5.0.js:561

 

tyler's picture

I see your code has a reference to cordova-1.5.0.js, you should probably upgrade to the latest version (2.6 as of today). It looks like your code is missing the cordova-1.5.0.js file, or is referencing it incorrectly. Either way, upgrading to the latest version may fix the issue.

Hi 

 

We are trying to upload a picture using phonegap's camera API And then link it to a node of particular content type using Drupal 7 services 3. Here is the code:



function onPhotoDataSuccess(imageData) {

                                // Uncomment to view the base64 encoded image data

                                // console.log(imageData);

                                // Get image handle

                                //

                                var smallImage = document.getElementById('smallImage');

                                // Unhide image elements

                                //

                                smallImage.style.display = 'block';


                                // Show the captured photo

                                // The inline CSS rules are used to resize the image

                                //

                                smallImage.src = "data:image/jpeg;base64," + imageData;

                                console.log(smallImage.src);

                }

               


Next user will click an upload button to upload in the server. Following is the code for upload


function  upLoad(){

                                console.log("in do sservice");

                                var imageSrc = document.getElementById("smallImage").src;

                               

                                console.log(imageSrc); ->able to get the image data

                                var data = {

                                                                  "file":{

                                                                    "file": imageSrc,

                                                                    "filename":"my_image12.jpg",

                                                                    "filepath":"sites/default/files/my_image12.jpg",

                                                                                                                                  }

                                                                };

                               

                                                                                 $.ajax({

                                                                      url: "http://jitll.com/xpert/rest/file.json",

                                                                      type: 'get',

                                                                                      data: data ,

                                                                      dataType: 'json',

                                                                      error: function(XMLHttpRequest, textStatus, errorThrown) {

                                                                        alert('error');

                                                                        console.log(JSON.stringify(XMLHttpRequest));

                                                                        console.log(JSON.stringify(textStatus));- After changing file permissions am getting null as an error.

                                                                        console.log(JSON.stringify(errorThrown));

For this, am getting a null error and not sure why. 

tyler's picture

Sorry, I'm not sure what is going on there. I would check my Drupal watchdog listing right after attempting the upload. I would also use the Devel module's dpm() function on the file create resource code inside the Services module to see what is going on.

I've also added a note about an issue with the Services module. That may be what is causing your issue: http://drupal.org/node/1666748

Image sucessfuly uploaded in to the server but when i try to open it its saying it is invalid image format.. Here is the image data am sending to the sever

{"uid":"1","filesize":"99999","filename":"whatever.jpg","file":"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2 .............."}

tyler's picture

Please see my reply above for more information, it may be helpful for this issue as well.

Hi Tylor!

Nice Tutorial, I got a problem when i try to save the image using the services "Could not write file to destination". Please give me an idea what is causing this error?

And please can you provide sample code for above tutorial (D7).

Thanks.

tyler's picture

Hello Haroon,

The example code above does contain the D7 code, it is hard to see though. The first block of code has the D7 version, but the code is nested in a comment:

"filepath":"sites/default/files/my_image.jpg", /* in D7 change this value to "public://my_image.jpg" */

Just use "public://my_image.jpg" for the filepath and you should be all set.

Hi Tyler, thanks for the post. I just want to know whether we can get multiple fid's with single call to file.json? Basically I want to include two files and send to file.json, and get two fid's. 

tyler's picture

I don't think you can POST more than one file at a time. If I were going to try it, I would POST a JSON object (or maybe an array) via the application/json Content-Type. The object/array would then contain multiple "file" JSON object that are equivalent to the ?data url.

How to create the file inside the "files" folder?

After successul post to Drupal.settings.basePath + 'my_services_endpoint/file.json' the retorned URI give all image data in base64 format, uri_full, filename etc, but the file is not created in the disk inside the files folder.

I think I can do this using create_raw but I can not make it work. I do not know how to pass parameters, or what parameters to pass.

tyler's picture

I haven't used the create raw service resource. I'd recommend posting a Support Request in the Services module's issue queue.

After POSTing to the file.json endpoint, you should get get back the file id and uri, if it is returning anything other than that, then it's probably being used incorrectly.

  1. Uncaught ReferenceError: edit_node_edit_field_photo_und_0_value_imagefield_source is not defined VM1430:1
    1. (anonymous function)VM1430:1
    2. b.event.dispatchjquery-1.9.1.min.js:3
    3. v.handl
    4.  
    5. Uncaught ReferenceError: edit_node_edit_field_photo_und_0_value_imagefield_destination_type is not defined VM1430:1
      1. (anonymous function)VM1430:1
      2. b.event.dispatchjquery-1.9.1.min.js:3
      3. v.handle
      4. The image select option and image capture buttion not working in phonegap camera api

Is there an updated version of the code taking into account the CSRF token? I've been trying to get this to work for days. Also how  do you get the base64 image string? Say you have a file input in a custom form you built and you just want to upload an image into drupal?

tyler's picture

As linked above, here's an example Drupal comment showing how to get the token, then making a POST call: https://www.drupal.org/node/2013781#comment-7507759

I can attach files, however, how do I detach files? Say, I have field_image on the user account entity. How do I unlink that file id from my user object?

tyler's picture

Maybe send a null fid. This issue may be related: https://www.drupal.org/node/2224803

Hi Tyler, do you know how to download a file using Drupal private files system? I'm trying cordova app, but i´m stuck cause I get the user session correctly but the download doesn't works. Thanks a lot, I appreciate your help so much.

Regards

tyler's picture

I do not, I haven't ever needed a private file system on a Drupal site in almost 10 years of working with Drupal.

Hi Tyler,

Great post!! 

How do you create an image and pass the alternate text along? I don't think it's possible but just want to make sure.

Thank you

tyler's picture

It should be possible, I think you'll just pass it along with the file id when you're done:

field_image: { und: [
 { fid: 123, alt: 'alt text', title: 'title text' }
]}

Add new comment