Stanisław Łabądź
Stanisław Łabądź

Reputation: 895

GAE Python Blobstore doesn't save filename containing unicode literals in Firefox only

I am developing an app which prompts the user to upload a file which is then available for download. Here is the download handler:

class ViewPrezentacje(blobstore_handlers.BlobstoreDownloadHandler, BaseHandler):
    def get(self,blob_key):
        blob_key = str(urllib.unquote(blob_key))
        blob_info=blobstore.BlobInfo.get(blob_key)
        self.send_blob(blob_info, save_as=urllib.quote(blob_info.filename.encode('utf-8')))

The file is downloaded with the correct file name (i.e. unicode literals are properly displayed) while using Chrome or IE, but in Firefox it is saved as a string of the form "%83%86%E3..." Is there any way to make it work properly in Firefox?

Upvotes: 0

Views: 61

Answers (1)

bobince
bobince

Reputation: 536567

Sending filenames with non-ASCII characters in attachments is fraught with difficulty, as the original specification was broken and browser behaviours have varied.

You shouldn't be %-encoding (urllib.quote) the filename; Firefox is right to offer it as literal % sequences as a result. IE's behaviour of %-decoding sequences in the filename is incorrect, even though Chrome eventually went on to copy it.

Ultimately the right way to send non-ASCII filenames is to use the mechanism specified in RFC6266, which ends up with a header that looks like this:

Content-Disposition: attachment; filename*=UTF-8''foo-%c3%a4-%e2%82%ac.html

However:

  • older browsers such as IE8 don't support it so if you care you should pass something as an ASCII-only filename= as well;
  • BlobstoreDownloadHandler doesn't know about this mechanism.

The bit of BlobstoreDownloadHandler that needs fixing is this inner function in send_blob:

def send_attachment(filename):
  if isinstance(filename, unicode):
    filename = filename.encode('utf-8')
  self.response.headers['Content-Disposition'] = (
      _CONTENT_DISPOSITION_FORMAT % filename)

which really wants to do:

rfc6266_filename = "UTF-8''" + urllib.quote(filename.encode('utf-8'))
fallback_filename = filename.encode('us-ascii', 'ignore')
self.response.headers['Content-Disposition'] = 'attachment; filename="%s"; filename*=%s' % (rfc6266_filename, fallback_filename)

but unfortunately being an inner function makes it annoying to try to fix in a subclass. You could:

  • override the whole of send_blob to replace the send_attachment inner function
  • or maybe you can write self.response.headers['Content-Disposition'] like this after calling send_blob? I'm not sure how GAE handles this
  • or, probably most practical of all, give up on having Unicode filenames for now until GAE fixes it

Upvotes: 1

Related Questions