Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions apps/oss/serializers/file.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from common.constants.authentication_type import AuthenticationType
from common.database_model_manage.database_model_manage import DatabaseModelManage
from common.exception.app_exception import AppApiException, AppUnauthorizedFailed, NotFound404
from django.db import DatabaseError
from django.db.models import QuerySet
from django.http import HttpResponse
from django.utils.translation import gettext
Expand Down Expand Up @@ -299,8 +300,16 @@ def get(self, mk_file_auth=None, with_valid=True):
file_type = file.file_name.split(".")[-1].lower()
content_type = mime_types.get(file_type, 'application/octet-stream')
encoded_filename = urllib.parse.quote(file.file_name)
# 获取文件内容
file_bytes = file.get_bytes()
# 获取文件内容。元数据还在但 PostgreSQL 大对象已丢失时,
# lo_get 会抛 DatabaseError(large object … does not exist),
# 不应以 500 把库原始报错透出给前端(#7356)。
try:
file_bytes = file.get_bytes()
except DatabaseError as e:
msg = str(e).lower()
if 'large object' in msg and 'does not exist' in msg:
raise NotFound404(404, _('File content not found')) from e
raise
file_size = len(file_bytes)

response = None
Expand Down
44 changes: 44 additions & 0 deletions apps/oss/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,47 @@ def test_get_url_retrieval_routes_pass_application_id(self):
'chat_oss_retrieval',
{'application_id': 'application-id'},
)


class MissingLargeObjectTests(SimpleTestCase):
"""#7356: metadata exists but Postgres large object is gone → 404, not 500."""

def test_operate_get_maps_missing_large_object_to_404(self):
from unittest.mock import MagicMock, patch

from django.db import DatabaseError

from common.exception.app_exception import NotFound404
from oss.serializers.file import FileSerializer

operate = FileSerializer.Operate(data={'id': '00000000-0000-0000-0000-000000000001'})
self.assertTrue(operate.is_valid())

fake_file = MagicMock()
fake_file.file_name = 'pic.png'
fake_file.get_bytes.side_effect = DatabaseError('large object 90563 does not exist')

with patch('oss.serializers.file.QuerySet') as qs, patch('oss.serializers.file.auth'):
qs.return_value.filter.return_value.first.return_value = fake_file
with self.assertRaises(NotFound404) as ctx:
operate.get(with_valid=False)
self.assertEqual(ctx.exception.status_code, 404)

def test_operate_get_rethrows_unrelated_database_error(self):
from unittest.mock import MagicMock, patch

from django.db import DatabaseError

from oss.serializers.file import FileSerializer

operate = FileSerializer.Operate(data={'id': '00000000-0000-0000-0000-000000000001'})
self.assertTrue(operate.is_valid())

fake_file = MagicMock()
fake_file.file_name = 'pic.png'
fake_file.get_bytes.side_effect = DatabaseError('connection already closed')

with patch('oss.serializers.file.QuerySet') as qs, patch('oss.serializers.file.auth'):
qs.return_value.filter.return_value.first.return_value = fake_file
with self.assertRaises(DatabaseError):
operate.get(with_valid=False)
Loading