jgilli
jgilli

Reputation: 106

POST image file from Google Apps Script to Google App Engine app that uses titan-files

I am using titan-files in my Google App Engine application to provide a filesystem-like layer to Data Store and Blob Store storage. I have a Google Apps Script that sends POST requests to my Google App Engine application to store the data in the blob store. It works fine when sending JSON data (as a string), but it doesn't work when sending binary data, like images.

I ended up trying to send base64 encoded data as a string and store it in the blob store. I thought that I could retrieve the data from the blob store and base64 decode it to get the original image. However, it doesn't seem to work.

Here's the code I am using to send the data from the Google Apps Script:

function pushFileToFilePath(file, destinationFilePath) {

 var payload = 
    {
      "content" : Utilities.base64Encode(file.getAs("image/jpeg").getBytes())
    };

  var options =
  {
      "method" : "post",
      "payload" : payload
  };

  var response = UrlFetchApp.fetch("https://myappname.appspot.com/_titan/file?path=" + destinationFilePath, options);
  if (response.getResponseCode() == 201)
  {
     return true;
  }

  return false;
}

The data is available in the blob store after the request is sent, and its size is in line with what I would expect from the results of base64-encoding the file. However, if I download the file from the blob store using the Google App Engine dashboard and base64-decode it using

openssl -d -in base_64_encoded_file.txt -out image.jpg

I get a zero-byte image.jpg file.

I also tried other ways to setup the payload object. For instance using:

var payload = 
{
  "content" : file.getAs("image/jpeg").getBytes()
};

gives a file of 18 bytes on the blob store, containing the string "[Ljava.lang.Object". Using:

 var payload = 
{
  "content" : file.getAs("image/jpeg")
};

I get the following exception in my Google App Engine logs:

Bad request:
Traceback (most recent call last):
  File "/base/data/home/apps/s~myappname/live-1.364814133265166345/titan/files/handlers.py", line 103, in post
    content, blob=blob, mime_type=mime_type, meta=meta, **method_kwargs)
  File "/base/data/home/apps/s~myappname/live-1.364814133265166345/titan/files/files.py", line 408, in Write
    content, blob = self._MaybeWriteToBlobstore(content, blob)
  File "/base/data/home/apps/s~myappname/live-1.364814133265166345/titan/files/files.py",   line 341, in _MaybeWriteToBlobstore
    blob = utils.WriteToBlobstore(content, old_blobinfo=old_blobinfo)
  File "/base/data/home/apps/s~myappname/live-1.364814133265166345/titan/common/utils.py", line 277, in WriteToBlobstore
    content_file = cStringIO.StringIO(content)
TypeError: expected read buffer, instance found

I tried using BytesIO to stream the content, but I got other exceptions.

Do you have any idea of how I could POST images (or more generally binary data) to titan-files?

Thank you!

UPDATE

I fixed my issue by changing the way the "content" data is retrieved from this:

content = self.request.str_POST.get('content')

to this:

content = self.request.get('content')

and by sending the data using the following code in my Google Apps Script:

 var payload = 
{
  "content" : file.getAs("image/jpeg")
};

that is, just setting "content" to be a Blob instance.

I don't know why I didn't try this in the first place :) Anyway, the change in titan-file is a hack and not a permanent solution, so I'll try to come up with a solid patch that handles both binary and non-binary data and submit it to the project.

Thank you!

Upvotes: 1

Views: 1393

Answers (1)

jgilli
jgilli

Reputation: 106

I created an issue in titan-files's issues tracker here: http://code.google.com/p/titan-files/issues/detail?id=1 . The patch attached to it fixes the issue, but it is not a clean and robust fix. Titan Files' maintainers will probably update the issue with a reference to a proper fix soon.

Upvotes: 1

Related Questions