upload: Factor out common code into zerver.lib.upload.

This commit is contained in:
Alex Vandiver
2024-06-20 16:11:59 +00:00
committed by Tim Abbott
parent c0d9a95aa8
commit c826d80061
4 changed files with 34 additions and 43 deletions

View File

@@ -20,7 +20,7 @@ from zerver.lib.thumbnail import (
resize_avatar, resize_avatar,
resize_emoji, resize_emoji,
) )
from zerver.lib.upload.base import ZulipUploadBackend from zerver.lib.upload.base import ZulipUploadBackend, create_attachment, sanitize_name
from zerver.models import Attachment, Message, Realm, RealmEmoji, ScheduledMessage, UserProfile from zerver.models import Attachment, Message, Realm, RealmEmoji, ScheduledMessage, UserProfile
@@ -83,14 +83,27 @@ def upload_message_attachment(
user_profile: UserProfile, user_profile: UserProfile,
target_realm: Optional[Realm] = None, target_realm: Optional[Realm] = None,
) -> str: ) -> str:
return upload_backend.upload_message_attachment( if target_realm is None:
uploaded_file_name, target_realm = user_profile.realm
path_id = upload_backend.generate_message_upload_path(
str(target_realm.id), sanitize_name(uploaded_file_name)
)
upload_backend.upload_message_attachment(
path_id,
uploaded_file_size, uploaded_file_size,
content_type, content_type,
file_data, file_data,
user_profile, user_profile,
target_realm=target_realm, target_realm,
) )
create_attachment(
uploaded_file_name,
path_id,
user_profile,
target_realm,
uploaded_file_size,
)
return f"/user_uploads/{path_id}"
def claim_attachment( def claim_attachment(

View File

@@ -59,13 +59,13 @@ class ZulipUploadBackend:
def upload_message_attachment( def upload_message_attachment(
self, self,
uploaded_file_name: str, path_id: str,
uploaded_file_size: int, uploaded_file_size: int,
content_type: Optional[str], content_type: Optional[str],
file_data: bytes, file_data: bytes,
user_profile: UserProfile, user_profile: UserProfile,
target_realm: Optional[Realm] = None, target_realm: Realm,
) -> str: ) -> None:
raise NotImplementedError raise NotImplementedError
def save_attachment_contents(self, path_id: str, filehandle: BinaryIO) -> None: def save_attachment_contents(self, path_id: str, filehandle: BinaryIO) -> None:

View File

@@ -12,7 +12,7 @@ from typing_extensions import override
from zerver.lib.mime_types import guess_type from zerver.lib.mime_types import guess_type
from zerver.lib.thumbnail import resize_avatar, resize_logo from zerver.lib.thumbnail import resize_avatar, resize_logo
from zerver.lib.timestamp import timestamp_to_datetime from zerver.lib.timestamp import timestamp_to_datetime
from zerver.lib.upload.base import ZulipUploadBackend, create_attachment, sanitize_name from zerver.lib.upload.base import ZulipUploadBackend
from zerver.lib.utils import assert_is_not_none from zerver.lib.utils import assert_is_not_none
from zerver.models import Realm, RealmEmoji, UserProfile from zerver.models import Realm, RealmEmoji, UserProfile
@@ -65,35 +65,28 @@ class LocalUploadBackend(ZulipUploadBackend):
return "/user_avatars/" return "/user_avatars/"
@override @override
def generate_message_upload_path(self, realm_id: str, uploaded_file_name: str) -> str: def generate_message_upload_path(self, realm_id: str, sanitized_file_name: str) -> str:
# Split into 256 subdirectories to prevent directories from getting too big # Split into 256 subdirectories to prevent directories from getting too big
return "/".join( return "/".join(
[ [
realm_id, realm_id,
format(random.randint(0, 255), "x"), format(random.randint(0, 255), "x"),
secrets.token_urlsafe(18), secrets.token_urlsafe(18),
sanitize_name(uploaded_file_name), sanitized_file_name,
] ]
) )
@override @override
def upload_message_attachment( def upload_message_attachment(
self, self,
uploaded_file_name: str, path_id: str,
uploaded_file_size: int, uploaded_file_size: int,
content_type: Optional[str], content_type: Optional[str],
file_data: bytes, file_data: bytes,
user_profile: UserProfile, user_profile: UserProfile,
target_realm: Optional[Realm] = None, target_realm: Realm,
) -> str: ) -> None:
if target_realm is None: write_local_file("files", path_id, file_data)
target_realm = user_profile.realm
path = self.generate_message_upload_path(str(target_realm.id), uploaded_file_name)
write_local_file("files", path, file_data)
create_attachment(uploaded_file_name, path, user_profile, target_realm, uploaded_file_size)
return "/user_uploads/" + path
@override @override
def save_attachment_contents(self, path_id: str, filehandle: BinaryIO) -> None: def save_attachment_contents(self, path_id: str, filehandle: BinaryIO) -> None:

View File

@@ -14,12 +14,7 @@ from typing_extensions import override
from zerver.lib.mime_types import guess_type from zerver.lib.mime_types import guess_type
from zerver.lib.thumbnail import resize_avatar, resize_logo from zerver.lib.thumbnail import resize_avatar, resize_logo
from zerver.lib.upload.base import ( from zerver.lib.upload.base import INLINE_MIME_TYPES, ZulipUploadBackend
INLINE_MIME_TYPES,
ZulipUploadBackend,
create_attachment,
sanitize_name,
)
from zerver.models import Realm, RealmEmoji, UserProfile from zerver.models import Realm, RealmEmoji, UserProfile
# Duration that the signed upload URLs that we redirect to when # Duration that the signed upload URLs that we redirect to when
@@ -196,44 +191,34 @@ class S3UploadBackend(ZulipUploadBackend):
return urljoin(self.public_upload_url_base, key) return urljoin(self.public_upload_url_base, key)
@override @override
def generate_message_upload_path(self, realm_id: str, uploaded_file_name: str) -> str: def generate_message_upload_path(self, realm_id: str, sanitized_file_name: str) -> str:
return "/".join( return "/".join(
[ [
realm_id, realm_id,
secrets.token_urlsafe(18), secrets.token_urlsafe(18),
sanitize_name(uploaded_file_name), sanitized_file_name,
] ]
) )
@override @override
def upload_message_attachment( def upload_message_attachment(
self, self,
uploaded_file_name: str, path_id: str,
uploaded_file_size: int, uploaded_file_size: int,
content_type: Optional[str], content_type: Optional[str],
file_data: bytes, file_data: bytes,
user_profile: UserProfile, user_profile: UserProfile,
target_realm: Optional[Realm] = None, target_realm: Realm,
) -> str: ) -> None:
if target_realm is None:
target_realm = user_profile.realm
s3_file_name = self.generate_message_upload_path(str(target_realm.id), uploaded_file_name)
url = f"/user_uploads/{s3_file_name}"
upload_image_to_s3( upload_image_to_s3(
self.uploads_bucket, self.uploads_bucket,
s3_file_name, path_id,
content_type, content_type,
user_profile, user_profile,
file_data, file_data,
settings.S3_UPLOADS_STORAGE_CLASS, settings.S3_UPLOADS_STORAGE_CLASS,
) )
create_attachment(
uploaded_file_name, s3_file_name, user_profile, target_realm, uploaded_file_size
)
return url
@override @override
def save_attachment_contents(self, path_id: str, filehandle: BinaryIO) -> None: def save_attachment_contents(self, path_id: str, filehandle: BinaryIO) -> None:
for chunk in self.uploads_bucket.Object(path_id).get()["Body"]: for chunk in self.uploads_bucket.Object(path_id).get()["Body"]: