Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

404 for files with non-latin filenames with StorageDownloadView and nginx optimization #110

Open
vmspike opened this issue Sep 2, 2015 · 2 comments

Comments

@vmspike
Copy link

vmspike commented Sep 2, 2015

I'm tried extremely simple configuration based on examples from manual.
Files with latin filenames served well but for files like ИП_ТУ.odt (Cyrillic) code 404 returned.
Some useful logs:

nginx error.log:
2015/09/03 00:28:37 [error] 14503#0: *7 open() "/full/path/%D0%98%D0%9F_%D0%A2%D0%A3.odt" failed (2: No such file or directory), client: 192.168.2.2, server: server, request: "GET /path/%D0%98%D0%9F_%D0%A2%D0%A3.odt HTTP/1.1", upstream: "uwsgi://unix:///some.sock", host: "server"

uwsgi emperror.log:
[pid: 14496|app: 0|req: 1/5] 192.168.2.2 () {44 vars in 814 bytes} [Thu Sep  3 00:28:37 2015] GET /path/%D0%98%D0%9F_%D0%A2%D0%A3.odt => generated 0 bytes in 131 msecs (HTTP/1.1 200) 5 headers in 424 bytes (1 switches on core 0) 

So the problem is quoted %XX-symbols, nginx trying to find files with exactly path (with %XX-symbols). Probably conversion is required before passing filename to web-server.

Also FileNotFoundError exception doesn't catch up if file not found.

  File "/myapp/venv/lib/python3.4/site-packages/django_downloadview/response.py", line 182, in default_headers
    headers['Content-Length'] = self.file.size
  File "/myapp/venv/lib/python3.4/site-packages/django_downloadview/files.py", line 107, in size
    return self.storage.size(self.name)
  File "/myapp/venv/lib/python3.4/site-packages/django/core/files/storage.py", line 311, in size
    return os.path.getsize(self.path(name))
  File "/myapp/venv/lib/python3.4/genericpath.py", line 50, in getsize
    return os.stat(filename).st_size
FileNotFoundError: [Errno 2] No such file or directory: '/full/path/ИП_ТУ.odttt'
@vmspike
Copy link
Author

vmspike commented Sep 4, 2015

For catch FileNotFoundError exception you may change BaseDownloadView get() method to

class BaseDownloadView(DownloadMixin, View):
    """A base :class:`DownloadMixin` that implements :meth:`get`."""
    def get(self, request, *args, **kwargs):
        """Handle GET requests: stream a file."""
        try:
            return self.render_to_response()
        except FileNotFoundError:
            return self.file_not_found_response()

@vmspike
Copy link
Author

vmspike commented Sep 4, 2015

As for quoting unicode issue temporary solution can be modifying XAccelRedirectResponse class this way:

from django.utils.encoding import uri_to_iri
class XAccelRedirectResponse(ProxiedDownloadResponse):
...
        # self['X-Accel-Redirect'] = redirect_url
        self['X-Accel-Redirect'] = bytes(uri_to_iri(redirect_url), 'utf-8')

This hack allow download files with non-ACSII name, but proposing filename for saving is %-quoted which is ugly.
For make request for non US-ACSII filename correct need to change content_disposition() function to use filename instead of utf8_filename:

def content_disposition(filename):
    # Fix incorrect filename propose for download
    if not filename:
        return 'attachment'
    ascii_filename = encode_basename_ascii(filename)
    utf8_filename = encode_basename_utf8(filename)
    if ascii_filename == utf8_filename:  # ASCII only.
        return "attachment; filename=\"{ascii}\"".format(ascii=ascii_filename)
    else:
        return "attachment; filename=\"{ascii}\"; filename*=UTF-8''{utf8}".format(
                    ascii=ascii_filename,
                    # utf8=utf8_filename)
                    utf8=filename)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant