Skip to content

Commit

Permalink
util: use the uploaded werkzeug.FileStorage class directly
Browse files Browse the repository at this point in the history
In newer versions of Werkzeug the uploaded file's stream is a
SpooledTemporaryFile. That class doesn't implement the .seekable method,
which is called by the Python zipfile library, and everything falls
over.

This patch avoids the issue by using the FileStorage class directly; as
of Werkzeug 0.15 it proxies this method correctly to whatever
object is backing the SpooledTemporaryFile. (At least according to the
relevant GH issue: pallets/werkzeug#1344 ).
  • Loading branch information
abrasive committed Aug 21, 2019
1 parent 89e2a62 commit bd7ba57
Show file tree
Hide file tree
Showing 2 changed files with 19 additions and 51 deletions.
2 changes: 1 addition & 1 deletion hostthedocs/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ def hmfd():
if request.method == 'POST':
if not request.files:
return abort(400, 'Request is missing a zip/tar file.')
uploaded_file = util.UploadedFile.from_request(request)
uploaded_file = util.file_from_request(request)
unpack_project(
uploaded_file,
request.form,
Expand Down
68 changes: 18 additions & 50 deletions hostthedocs/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,56 +10,24 @@

logger = logging.getLogger()


class UploadedFile(object):
"""
UploadedFile represents a file uploaded during a POST request.
def file_from_request(request):
"""
Get the uploaded file from a POST request, which should contain exactly one file.
def __init__(self, filename, stream):
"""Instantiate an UploadedFile
:param str filename: The name of the file.
:param stream: The open file stream.
"""
self._filename = filename
self._stream = stream

@classmethod
def from_request(cls, request):
"""
Instantiate an UploadedFile from the first file in a request.
:param werkzeug.wrappers.BaseRequest request: The POST request.
:return: The instantiated UploadedFile.
:raises ValueError: if no files exist within the request.
"""
uploaded_files = list(request.files.values())
if len(uploaded_files) > 1:
logger.warning(
'Only one file can be uploaded for each request. '
'Only the first file will be used.'
)
elif len(uploaded_files) == 0:
raise ValueError('Request does not contain uploaded file')

current_file = uploaded_files[0]
return cls(current_file.filename, current_file.stream)

def get_filename(self):
return self._filename

def get_stream(self):
return self._stream

def close(self):
"""close the file stream
"""
try:
self._stream.close()
except:
pass
:param werkzeug.wrappers.BaseRequest request: The POST request.
:return: The instantiated UploadedFile.
:raises ValueError: if no files exist within the request.
"""
uploaded_files = list(request.files.values())
if len(uploaded_files) > 1:
logger.warning(
'Only one file can be uploaded for each request. '
'Only the first file will be used.'
)
elif len(uploaded_files) == 0:
raise ValueError('Request does not contain uploaded file')

return uploaded_files[0]

class FileExpander(object):
"""
Expand Down Expand Up @@ -97,11 +65,11 @@ def detect_compression_method(cls, filename):
raise ValueError('Unknown compression method for %s' % filename)

def __enter__(self):
method = self.detect_compression_method(self._file.get_filename())
method = self.detect_compression_method(self._file.filename)
if method == 'zip':
self._handle = zipfile.ZipFile(self._file.get_stream())
self._handle = zipfile.ZipFile(self._file)
elif method == 'tar':
self._handle = tarfile.open(fileobj=self._file.get_stream(), mode='r:*')
self._handle = tarfile.open(fileobj=self._file, mode='r:*')
else:
raise ValueError('Unsupported method %s' % method)

Expand Down

0 comments on commit bd7ba57

Please # to comment.