diff --git a/store/app/crud/artifacts.py b/store/app/crud/artifacts.py index 4f1712ec..8934a5ea 100644 --- a/store/app/crud/artifacts.py +++ b/store/app/crud/artifacts.py @@ -53,10 +53,10 @@ def _crop_image(self, image: Image.Image, size: tuple[int, int]) -> io.BytesIO: image_bytes.seek(0) return image_bytes - async def _upload_cropped_image(self, image: Image.Image, image_id: str, size: ArtifactSize) -> None: + async def _upload_cropped_image(self, image: Image.Image, name: str, image_id: str, size: ArtifactSize) -> None: image_bytes = self._crop_image(image, SizeMapping[size]) - name = get_artifact_name(image_id, "image", size) - await self._upload_to_s3(image_bytes, name, "image/png") + filename = get_artifact_name(image_id, "image", size) + await self._upload_to_s3(image_bytes, name, filename, "image/png") async def _upload_image( self, @@ -83,6 +83,7 @@ async def _upload_image( *( self._upload_cropped_image( image=image, + name=name, image_id=artifact.id, size=size, ) @@ -115,7 +116,7 @@ async def _upload_raw_artifact( description=description, ) await asyncio.gather( - self._upload_to_s3(file, get_artifact_name(artifact.id, artifact_type), content_type), + self._upload_to_s3(file, name, get_artifact_name(artifact.id, artifact_type), content_type), self._add_item(artifact), ) return artifact diff --git a/store/app/crud/base.py b/store/app/crud/base.py index 0c78e917..e5bdce62 100644 --- a/store/app/crud/base.py +++ b/store/app/crud/base.py @@ -298,11 +298,12 @@ async def _update_item(self, item_id: str, item_class: type[T], new_values: dict AttributeUpdates={k: {"Value": v, "Action": "PUT"} for k, v in new_values.items() if k != "id"}, ) - async def _upload_to_s3(self, data: io.BytesIO | BinaryIO, filename: str, content_type: str) -> None: + async def _upload_to_s3(self, data: io.BytesIO | BinaryIO, name: str, filename: str, content_type: str) -> None: """Uploads some data to S3. Args: data: The data to upload to S3. + name: The filename you want users who download the artifact to receive. filename: The resulting filename in S3 (should be unique). content_type: The file content type, for CloudFront to provide in the file header when the user retrieves it. @@ -311,7 +312,10 @@ async def _upload_to_s3(self, data: io.BytesIO | BinaryIO, filename: str, conten await bucket.upload_fileobj( data, f"{settings.s3.prefix}{filename}", - ExtraArgs={"ContentType": content_type}, + ExtraArgs={ + "ContentType": content_type, + "ContentDisposition": f'attachment; filename="{name}"', + }, ) async def _delete_from_s3(self, filename: str) -> None: