|
27 | 27 | __all__ = ("PyMongo", "ASCENDING", "DESCENDING", "BSONObjectIdConverter", "BSONProvider")
|
28 | 28 |
|
29 | 29 | import hashlib
|
| 30 | +import warnings |
30 | 31 | from mimetypes import guess_type
|
31 | 32 | from typing import Any
|
32 | 33 |
|
@@ -183,12 +184,24 @@ def get_upload(filename):
|
183 | 184 | response.headers["Content-Disposition"] = f"attachment; filename={filename}"
|
184 | 185 | response.content_length = fileobj.length
|
185 | 186 | response.last_modified = fileobj.upload_date
|
186 |
| - # Compute the sha1 sum of the file for the etag. |
187 |
| - pos = fileobj.tell() |
188 |
| - raw_data = fileobj.read() |
189 |
| - fileobj.seek(pos) |
190 |
| - digest = hashlib.sha1(raw_data).hexdigest() |
191 |
| - response.set_etag(digest) |
| 187 | + |
| 188 | + # GridFS does not manage its own checksum. |
| 189 | + # Try to use a sha1 sum that we have added during a save_file. |
| 190 | + # Fall back to a legacy md5 sum if it exists. |
| 191 | + # Otherwise, compute the sha1 sum directly. |
| 192 | + try: |
| 193 | + etag = fileobj.sha1 |
| 194 | + except AttributeError: |
| 195 | + with warnings.catch_warnings(): |
| 196 | + warnings.simplefilter("ignore") |
| 197 | + etag = fileobj.md5 |
| 198 | + if etag is None: |
| 199 | + pos = fileobj.tell() |
| 200 | + raw_data = fileobj.read() |
| 201 | + fileobj.seek(pos) |
| 202 | + etag = hashlib.sha1(raw_data).hexdigest() |
| 203 | + response.set_etag(etag) |
| 204 | + |
192 | 205 | response.cache_control.max_age = cache_for
|
193 | 206 | response.cache_control.public = True
|
194 | 207 | response.make_conditional(request)
|
@@ -237,5 +250,23 @@ def save_upload(filename):
|
237 | 250 | db_obj = self.db
|
238 | 251 | assert db_obj is not None, "Please initialize the app before calling save_file!"
|
239 | 252 | storage = GridFS(db_obj, base)
|
240 |
| - id = storage.put(fileobj, filename=filename, content_type=content_type, **kwargs) |
241 |
| - return id |
| 253 | + |
| 254 | + # GridFS does not manage its own checksum, so we attach a sha1 to the file |
| 255 | + # for use as an etag. |
| 256 | + hashingfile = _Wrapper(fileobj) |
| 257 | + with storage.new_file(filename=filename, content_type=content_type, **kwargs) as grid_file: |
| 258 | + grid_file.write(hashingfile) |
| 259 | + grid_file.sha1 = hashingfile.hash.hexdigest() |
| 260 | + return grid_file._id |
| 261 | + |
| 262 | + |
| 263 | +class _Wrapper: |
| 264 | + def __init__(self, file): |
| 265 | + self.file = file |
| 266 | + self.hash = hashlib.sha1() |
| 267 | + |
| 268 | + def read(self, n): |
| 269 | + data = self.file.read(n) |
| 270 | + if data: |
| 271 | + self.hash.update(data) |
| 272 | + return data |
0 commit comments