8

I am trying to write a file sharing application that exposes a REST interface.

The library I am using, Flask-RESTful only supports returning JSON by default. Obviously attempting to serve binary data over JSON is not a good idea at all.

What is the most "RESTful" way of serving up binary data through a GET method? It appears possible to extend Flask-RESTful to support returning different data representations besides JSON but the documentation is scarce and I'm not sure if it's even the best approach.

Ayrx
  • 2,092
  • 5
  • 26
  • 34
  • It is really broad question. You extend Flask-RESTful. Nothing stops you from building [Response object](http://flask.pocoo.org/docs/api/#response-objects) from scratch. If data is static it is probably better to handle that using web-server. Could you add some details. – zero323 Nov 10 '13 at 10:27

3 Answers3

10

The approach suggested in the Flask-RESTful documentation is to declare our supported representations on the Api object so that it can support other mediatypes. The mediatype we are looking for is application/octet-stream.

First, we need to write a representation function:

from flask import Flask, send_file, safe_join
from flask_restful import Api

app = Flask(__name__)
api = Api(app)

@api.representation('application/octet-stream')
def output_file(data, code, headers):
    filepath = safe_join(data["directory"], data["filename"])

    response = send_file(
        filename_or_fp=filepath,
        mimetype="application/octet-stream",
        as_attachment=True,
        attachment_filename=data["filename"]
    )
    return response

What this representation function does is to convert the data, code, headers our method returns into a Response object with mimetype application/octet-stream. Here we use send_file function to construct this Response object.

Our GET method can be something like:

from flask_restful import Resource

class GetFile(Resource):
    def get(self, filename):
        return {
            "directory": <Our file directory>,
            "filename": filename
        }

And that's all the coding we need. When sending this GET request, we need to change the Accept mimetype to Application/octet-stream so that our API will call the representation function. Otherwise it will return the JSON data as by default.

There's an xml example on github

I know this question was asked 7 years ago so it probably doesn't matter any more to @Ayrx. Hope it helps to whoever drops by.

AlpacaMax
  • 388
  • 3
  • 10
  • 1
    Thank you for your precise answer with example code. It helped a lot and saved time. I wonder why there is no concrete example in the restful documentation. – Vinoj John Hosan May 05 '21 at 12:23
5

As long as you're setting the Content-Type header accordingly and respecting the Accept header sent by the client, you're free to return any format you want. You can just have a view that returns your binary data with the application/octet-stream content type.

Pedro Werneck
  • 40,902
  • 7
  • 64
  • 85
0

After lot of trials and experiments, including hours of browsing to make the Response class as Single Responsible down loader

class DownloadResource(Resource):
    def get(self):
        item_list = dbmodel.query.all()
        item_list = [item.image for item in item_list]

        data = json.dumps({'items':item_list})
        response = make_response(data)
        response.headers['Content-Type'] = 'text/json'
        response.headers['Content-Disposition'] = 'attachment; filename=selected_items.json'
        return response

Change your filename and content type to support the format you want.

Vinoj John Hosan
  • 6,448
  • 2
  • 41
  • 37