Description
The MinIO driver uploads all media files without setting the content_type
parameter. As a result, MinIO falls back to its default value
application/octet-stream for every object — regardless of whether the file
is a JPEG, PNG, PDF, MP4 or anything else.
This causes problems whenever the bucket is consumed by clients that rely on
the HTTP Content-Type header to render the content inline:
- Browsers offer the file as a download instead of displaying it
- QGIS HTML widgets / map tips cannot render
<img> references to the bucket
- Embedding photos in WebGIS / popups / reports requires a workaround
- The MinIO response additionally includes
X-Content-Type-Options: nosniff,
which prevents clients from guessing the type from the payload
A typical response header for an uploaded JPG currently looks like this:
content-type: application/octet-stream
x-content-type-options: nosniff
content-length: 326330
Even though the file is a perfectly valid JPEG.
Reproduction
-
Set up media-sync with a MinIO backend (driver: minio)
-
Sync a Mergin Maps project that contains photos (e.g. captured with the
Mergin Maps mobile app)
-
Inspect the response headers of any uploaded file in the bucket, e.g.:
curl -I https://<minio-host>/<bucket>/fotos/example.jpg
Result: Content-Type: application/octet-stream
Root cause
In drivers.py, the MinioDriver.upload_file() method calls
fput_object() without a content_type:
res = self.client.fput_object(self.bucket, obj_path, src)
The default for that parameter in the MinIO Python SDK is
application/octet-stream, so every object ends up with that type.
Proposed fix
Use Python's standard mimetypes module to derive the type from the file
extension, falling back to the previous default if unknown:
import mimetypes
def upload_file(self, src, obj_path):
if self.bucket_subpath:
obj_path = f"{self.bucket_subpath}/{obj_path}"
try:
content_type, _ = mimetypes.guess_type(src)
if content_type is None:
content_type = "application/octet-stream"
res = self.client.fput_object(
self.bucket, obj_path, src, content_type=content_type
)
dest = self.base_url + "/" + res.object_name
except S3Error as e:
raise DriverError("MinIO driver error: " + str(e))
return dest
This is a minimal, dependency-free change (mimetypes is part of the Python
standard library) that fixes the issue for all common media file types
produced by Mergin Maps (jpg, jpeg, png, heic, mp4, pdf, …).
I'm happy to open a PR if that helps.
Environment
- media-sync: latest
main
- Backend: MinIO (S3-compatible)
- Mergin Maps cloud
Description
The MinIO driver uploads all media files without setting the
content_typeparameter. As a result, MinIO falls back to its default value
application/octet-streamfor every object — regardless of whether the fileis a JPEG, PNG, PDF, MP4 or anything else.
This causes problems whenever the bucket is consumed by clients that rely on
the HTTP
Content-Typeheader to render the content inline:<img>references to the bucketX-Content-Type-Options: nosniff,which prevents clients from guessing the type from the payload
A typical response header for an uploaded JPG currently looks like this:
Even though the file is a perfectly valid JPEG.
Reproduction
Set up media-sync with a MinIO backend (driver:
minio)Sync a Mergin Maps project that contains photos (e.g. captured with the
Mergin Maps mobile app)
Inspect the response headers of any uploaded file in the bucket, e.g.:
Result:
Content-Type: application/octet-streamRoot cause
In
drivers.py, theMinioDriver.upload_file()method callsfput_object()without acontent_type:The default for that parameter in the MinIO Python SDK is
application/octet-stream, so every object ends up with that type.Proposed fix
Use Python's standard
mimetypesmodule to derive the type from the fileextension, falling back to the previous default if unknown:
This is a minimal, dependency-free change (mimetypes is part of the Python
standard library) that fixes the issue for all common media file types
produced by Mergin Maps (jpg, jpeg, png, heic, mp4, pdf, …).
I'm happy to open a PR if that helps.
Environment
main