Repository: wagtail/wagtail

Issue:
Documents: This backend doesn't support absolute paths. (Update to 1.0b2)
## 1. Bug since Wagtail update

I recently upgraded to 1.0b2 (from 1.0b1) and now, when I try to access I document I uploaded via a `wagtaildocs.Document` field, I get the following error:

```
NotImplementedError at /documents/3/headphones.svg
This backend doesn't support absolute paths.
```

The field is specified as:

```
svg_mask = models.ForeignKey(
    verbose_name=u"Mask (SVG)",
    to='wagtaildocs.Document',
    null=True,
    blank=True,
    on_delete=models.SET_NULL,
    related_name='+'
)
```

Is this a wagtail bug or a configuration error?
## 2. Unexpected behaviour

I would like to serve the svg as a `image/svg+xml`, not as a stream (which was the case in 1.0b1). I set the mimetype in my settings as follows:

```
mimetypes.add_type("image/svg+xml", ".svg", True)
mimetypes.add_type("image/svg+xml", ".svgz", True)
```

Yet (in 1.0b1) it always got served as a stream. I couldn't test it in 1.0b2 yet, because of the above problem.

Thank you in advance.



Discussion:
Are you using a non-standard file storage backend? If so, what do you have as your `DEFAULT_FILE_STORAGE` setting?

Yes, we are using S3:

```
DEFAULT_FILE_STORAGE = 'storages.backends.s3boto.S3BotoStorage'
STATICFILES_STORAGE = DEFAULT_FILE_STORAGE
```

Do I need to change any settings for this?

@gasman Do you have any ideas for "2. Unexpected behaviour"?

The streaming behaviour isn't dictated by the mime type. You can switch it off globally though by installing `django-sendfile` and adding the following to your settings:

```
SENDFILE_BACKEND = 'sendfile.backends.simple'
```

@kaedroho: Thank you very much. Is there any way to selectively disable it (ie. for a specific field in the model).
If not, I'll probably go with this.

@nilssolanki Please can you open a separate issue for the "2. Unexpected behaviour" point?


Resolving patch:
diff --git a/wagtail/wagtaildocs/views/serve.py b/wagtail/wagtaildocs/views/serve.py
--- a/wagtail/wagtaildocs/views/serve.py
+++ b/wagtail/wagtaildocs/views/serve.py
@@ -1,5 +1,9 @@
 from django.shortcuts import get_object_or_404
 from django.conf import settings
+from django.http import StreamingHttpResponse, BadHeaderError
+
+from unidecode import unidecode
+from wsgiref.util import FileWrapper
 
 from wagtail.utils.sendfile import sendfile
 from wagtail.utils import sendfile_streaming_backend
@@ -13,8 +17,40 @@ def serve(request, document_id, document_filename):
     # Send document_served signal
     document_served.send(sender=Document, instance=doc, request=request)
 
-    if hasattr(settings, 'SENDFILE_BACKEND'):
-        return sendfile(request, doc.file.path, attachment=True, attachment_filename=doc.filename)
+    try:
+        local_path = doc.file.path
+    except NotImplementedError:
+        local_path = None
+
+    if local_path:
+
+        # Use wagtail.utils.sendfile to serve the file;
+        # this provides support for mimetypes, if-modified-since and django-sendfile backends
+
+        if hasattr(settings, 'SENDFILE_BACKEND'):
+            return sendfile(request, local_path, attachment=True, attachment_filename=doc.filename)
+        else:
+            # Fallback to streaming backend if user hasn't specified SENDFILE_BACKEND
+            return sendfile(request, local_path, attachment=True, attachment_filename=doc.filename, backend=sendfile_streaming_backend.sendfile)
+
     else:
-        # Fallback to streaming backend if user hasn't specified SENDFILE_BACKEND
-        return sendfile(request, doc.file.path, attachment=True, attachment_filename=doc.filename, backend=sendfile_streaming_backend.sendfile)
+
+        # We are using a storage backend which does not expose filesystem paths
+        # (e.g. storages.backends.s3boto.S3BotoStorage).
+        # Fall back on pre-sendfile behaviour of reading the file content and serving it
+        # as a StreamingHttpResponse
+
+        wrapper = FileWrapper(doc.file)
+        response = StreamingHttpResponse(wrapper, content_type='application/octet-stream')
+
+        try:
+            response['Content-Disposition'] = 'attachment; filename=%s' % doc.filename
+        except BadHeaderError:
+            # Unicode filenames can fail on Django <1.8, Python 2 due to
+            # https://code.djangoproject.com/ticket/20889 - try with an ASCIIfied version of the name
+            response['Content-Disposition'] = 'attachment; filename=%s' % unidecode(doc.filename)
+
+        # FIXME: storage backends are not guaranteed to implement 'size'
+        response['Content-Length'] = doc.file.size
+
+        return response


Test patch: