Skip to content

Commit

Permalink
Support partial request for media resource
Browse files Browse the repository at this point in the history
  • Loading branch information
marschhuynh authored and nicolaiarocci committed Apr 5, 2018
1 parent f7fe3c1 commit 0f0d047
Show file tree
Hide file tree
Showing 2 changed files with 94 additions and 22 deletions.
22 changes: 22 additions & 0 deletions docs/features.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1776,6 +1776,28 @@ configuration. Enable ``AUTO_COLLAPSE_MULTI_KEYS`` and ``AUTO_CREATE_LISTS``
to make this possible. This allows to send multiple values for one key in
``multipart/form-data`` requests and in this way upload a list of files.
.. _partial_request:
Partial request for media resource
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The partial request provides an ability to download a part of a file.
You can also pause and resume when you are downloading without restarting
your download. To use it, make sure you have ``Range`` in your request header.
.. code-block:: console
$ curl http://localhost/media/yourfilename -i -H "Range: bytes=0-10"
HTTP/1.1 206 PARTIAL CONTENT
Date: Sun, 20 Aug 2017 14:26:42 GMT
Content-Type: audio/mp4
Content-Length: 11
Connection: keep-alive
Content-Range: bytes 0-10/23671
Last-Modified: Sat, 19 Aug 2017 03:25:36 GMT
Accept-Ranges: bytes
ftypmp4%
.. _geojson_feature:
GeoJSON
Expand Down
94 changes: 72 additions & 22 deletions eve/endpoints.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
:copyright: (c) 2017 by Nicola Iarocci.
:license: BSD, see LICENSE for more details.
"""
import re

from bson import tz_util
from flask import abort, request, current_app as app, Response

Expand Down Expand Up @@ -175,28 +177,76 @@ def media_endpoint(_id):
.. versionadded:: 0.6
"""
file_ = app.media.get(_id)
if file_ is None:
return abort(404)

if_modified_since = weak_date(request.headers.get('If-Modified-Since'))
if if_modified_since is not None:
if if_modified_since.tzinfo is None:
if_modified_since = if_modified_since.replace(
tzinfo=tz_util.utc)

if if_modified_since > file_.upload_date:
return Response(status=304)

headers = {
'Last-Modified': date_to_rfc1123(file_.upload_date),
'Content-Length': file_.length,
}

response = Response(file_, headers=headers, mimetype=file_.content_type,
direct_passthrough=True)

return response
range_header = request.headers.get('Range', None)
if not range_header:
file_ = app.media.get(_id)
if file_ is None:
return abort(404)

if_modified_since = weak_date(request.headers.get('If-Modified-Since'))
if if_modified_since is not None:
if if_modified_since.tzinfo is None:
if_modified_since = if_modified_since.replace(
tzinfo=tz_util.utc)

if if_modified_since > file_.upload_date:
return Response(status=304)

headers = {
'Last-Modified': date_to_rfc1123(file_.upload_date),
'Content-Length': file_.length,
'Accept-Ranges': 'bytes',
}

response = Response(
file_,
status=200,
headers=headers,
mimetype=file_.content_type,
direct_passthrough=True
)

return response
else:
file_ = app.media.get(_id)
size = file_.length
byte1, byte2 = 0, None

m = re.search('(\d+)-(\d*)', range_header)
g = m.groups()

if g[0]:
byte1 = int(g[0])
if g[1]:
byte2 = int(g[1])

length = size - byte1
if byte2 is not None:
length = byte2 - byte1 + 1

data = None
file_.seek(byte1)
data = file_.read(length)

headers = {
'Last-Modified': date_to_rfc1123(file_.upload_date),
'Content-Length': file_.length,
'Accept-Ranges': 'bytes',
'Content-Range': 'bytes {0}-{1}/{2}'.format(
byte1,
byte1 + length - 1,
size
),
}

response = Response(
data,
206,
headers=headers,
mimetype=file_.content_type,
direct_passthrough=True)

return response


@requires_auth('resource')
Expand Down

0 comments on commit 0f0d047

Please sign in to comment.