Repository: ckan/ckan

Issue:
Embed file in another site problem with mime type
**CKAN version**
ckan_version": "2.9"

**Describe the bug**
Embed file in another site from ckan force to download because mime type is incorrect, always is embed with Content-Type: application/octet-stream

**Steps to reproduce**
Load PDF in ckan and use embed to display in another site, when you load page with embed file try download and not display pdf.

**Expected behavior**
Display embed pdf



Discussion:
This is why I had to do the ugly NGINX workaround in the built-in PDF resource viewer PR #5541.

Hopefully, fixing this will allow that PR to be merged.

I had a look at this while working on the upgrade of ckanext-pdfview and it's potential replacement with builtin viewers submitted by @jqnatividad. 
I do now believe that this is a bug on the CKAN side, or at least behavior that could be improved. As the reporter suggests all uploaded files that are downloaded from CKAN are returned with a `Content-Type: application/octet-stream` regardless of format. With the main use case of users downloading files from the browser this is generally fine as the browsers will generally auto-detect the format from the file name, but when integrating with other tools things start to break. For instance in the case described here, where you are embedding a CKAN-hosted PDF in another site, or trying to use the new builtin viewer in #5541 in Chrome.

We are extracting and storing the mime type [when a file is uploaded](https://github.com/ckan/ckan/blob/bb3b80eef5ff26ed4d0ef413a599a09b4eb0cef4/ckan/logic/action/update.py#L284:L286) so it makes sense to use it when serving the file in the [download endpoint](https://github.com/ckan/ckan/blob/bb3b80eef5ff26ed4d0ef413a599a09b4eb0cef4/ckan/views/resource.py#L175), eg:

```python
    if rsc.get(u'url_type') == u'upload':
        upload = uploader.get_resource_uploader(rsc)
        filepath = upload.get_path(rsc[u'id'])
        resp =  flask.send_file(filepath)
        if rsc.get('mimetype'):
            resp.headers['Content-Type'] = rsc['mimetype']
        return resp

```
Note that this does not affect the disposition of the downloaded file ( `Content-disposition` header), soI don't think we would be breaking anything with this change and we could consider it for backporting to 2.9

Any thoughts on this?






@pdelboca clearing your assignation so this gets raised in the next meeting

Resolving patch:
diff --git a/ckan/views/resource.py b/ckan/views/resource.py
--- a/ckan/views/resource.py
+++ b/ckan/views/resource.py
@@ -172,7 +172,11 @@ def download(package_type, id, resource_id, filename=None):
     if rsc.get(u'url_type') == u'upload':
         upload = uploader.get_resource_uploader(rsc)
         filepath = upload.get_path(rsc[u'id'])
-        return flask.send_file(filepath)
+        resp = flask.send_file(filepath)
+        if rsc.get('mimetype'):
+            resp.headers['Content-Type'] = rsc['mimetype']
+        return resp
+
     elif u'url' not in rsc:
         return base.abort(404, _(u'No download is available'))
     return h.redirect_to(rsc[u'url'])


Test patch:
diff --git a/ckan/tests/controllers/test_package.py b/ckan/tests/controllers/test_package.py
--- a/ckan/tests/controllers/test_package.py
+++ b/ckan/tests/controllers/test_package.py
@@ -1065,6 +1065,29 @@ def test_anonymous_users_cannot_edit_resource(self, app):
             )
 
 
+@pytest.mark.usefixtures("clean_db", "with_plugins", "with_request_context")
+class TestResourceDownload(object):
+
+    def test_resource_download_content_type(self, create_with_upload, app):
+
+        dataset = factories.Dataset()
+        resource = create_with_upload(
+            u"hello,world", u"file.csv",
+            package_id=dataset[u"id"]
+        )
+
+        assert resource[u"mimetype"] == u"text/csv"
+        url = url_for(
+            u"{}_resource.download".format(dataset[u"type"]),
+            id=dataset[u"id"],
+            resource_id=resource[u"id"],
+        )
+
+        response = app.get(url)
+
+        assert response.headers[u"Content-Type"] == u"text/csv"
+
+
 @pytest.mark.ckan_config("ckan.plugins", "image_view")
 @pytest.mark.usefixtures("clean_db", "with_plugins", "with_request_context")
 class TestResourceView(object):