Skip to content

Commit cc4d5c6

Browse files
committed
using image library for reading file and determining media type
1 parent 4b8f337 commit cc4d5c6

File tree

5 files changed

+39
-57
lines changed

5 files changed

+39
-57
lines changed

app/storage_manager.py

+6-19
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
# -*- coding: utf-8 -*-
2-
import json
32
import os
43

4+
from PIL import Image
5+
56
from .settings import settings
67

78

@@ -16,30 +17,16 @@ def path_for_uuid(self, uuid: list[str]) -> str:
1617
def uuid_exists(self, uuid: list[str]) -> bool:
1718
return os.path.exists(self.path_for_uuid(uuid))
1819

19-
def save_image(self, uuid: list[str], file_content: bytes, data: str | None = None) -> None:
20+
def save_image(self, uuid: list[str], file_content: bytes) -> None:
2021
folder = self.path_for_uuid(uuid)
2122
os.makedirs(folder)
2223
with open(os.path.join(folder, "file"), "wb") as f:
2324
f.write(file_content)
24-
if data:
25-
with open(os.path.join(folder, "data"), "w") as f:
26-
f.write(data)
27-
28-
def _read_data(self, uuid: list[str]) -> dict[str, str]:
29-
path = os.path.join(self.path_for_uuid(uuid), "data")
30-
if not os.path.exists(path):
31-
return {}
32-
with open(path, "r") as f:
33-
return json.loads(f.read())
34-
35-
def _read_file(self, uuid: list[str]) -> bytes:
36-
with open(os.path.join(self.path_for_uuid(uuid), "file"), "rb") as f:
37-
return f.read()
38-
39-
def get_file(self, uuid: list[str]) -> tuple[bytes, dict]:
25+
26+
def get_image(self, uuid: list[str]) -> Image.Image:
4027
if not self.uuid_exists(uuid):
4128
raise StorageManagerException("Not Found")
42-
return self._read_file(uuid), self._read_data(uuid)
29+
return Image.open(os.path.join(self.path_for_uuid(uuid), "file"))
4330

4431

4532
def get_storage_manager() -> StorageManager:

app/views.py

+7-8
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
# -*- coding: utf-8 -*-
22
import base64
3-
import json
3+
import io
44
import logging
55

66
from fastapi import APIRouter, HTTPException
7+
from PIL import Image
78
from pydantic import BaseModel
89
from starlette import status
910
from starlette.requests import Request
@@ -50,11 +51,6 @@ async def post_image(
5051
get_storage_manager().save_image(
5152
uuid=[client.id, filename],
5253
file_content=base64.b64decode(params.base64),
53-
data=json.dumps(
54-
dict(
55-
mimetype="image/jpeg",
56-
)
57-
),
5854
)
5955

6056
return dict(
@@ -66,8 +62,11 @@ async def post_image(
6662
@router_api.get("/image/{client_id}/{uuid}")
6763
async def get_image(client_id: str, uuid: str) -> Response:
6864
try:
69-
content, data = get_storage_manager().get_file([client_id, uuid])
70-
return Response(content=content, media_type=data.get("mimetype"))
65+
image = get_storage_manager().get_image([client_id, uuid])
66+
img_stream = io.BytesIO()
67+
assert image.format is not None
68+
image.save(img_stream, image.format, quality="keep")
69+
return Response(content=img_stream.getvalue(), media_type=Image.MIME[image.format])
7170
except StorageManagerException as e:
7271
raise HTTPException(
7372
status_code=status.HTTP_404_NOT_FOUND,

pyproject.toml

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ fastapi = "~0.111"
1010
uvicorn = "~0.30"
1111
base58 = "2.1.1"
1212
pydantic-settings = "~2.2.1"
13+
pillow = "~10.3.0"
1314

1415
[tool.poetry.group.test]
1516
optional = true

tests/test_storage_manager.py

+15-21
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
# -*- coding: utf-8 -*-
2+
import io
23
import os
34

45
import pytest
6+
from PIL import Image, ImageChops
57

68
from app import storage_manager
79
from app.settings import Settings
@@ -14,26 +16,22 @@ def _setup(self, upload_folder, monkeypatch):
1416
self.upload_folder = upload_folder
1517
self.manager = storage_manager.StorageManager()
1618

17-
def check_save(self, uuid, data=None):
19+
self.image = Image.new(mode="RGB", size=(3, 3))
20+
img_stream = io.BytesIO()
21+
self.image.save(img_stream, format="jpeg")
22+
self.image_byte_array = img_stream.getvalue()
23+
24+
def check_save(self, uuid):
1825
folder = os.path.join(self.upload_folder, uuid)
1926
file_path = os.path.join(folder, "file")
20-
data_path = os.path.join(folder, "data")
2127

2228
assert os.path.isdir(folder)
2329
assert os.path.isfile(file_path)
2430
with open(file_path, "rb") as file:
25-
assert file.read() == b"abcdef"
26-
if data is not None:
27-
assert os.path.isfile(data_path)
28-
with open(data_path, "r") as file:
29-
assert file.read() == data
31+
assert file.read() == self.image_byte_array
3032

3133
def test_save_image_ok(self):
32-
self.manager.save_image(["some_uuid"], b"abcdef", "some_data")
33-
self.check_save("some_uuid", "some_data")
34-
35-
def test_save_image_no_data_ok(self):
36-
self.manager.save_image(["some_uuid"], b"abcdef")
34+
self.manager.save_image(["some_uuid"], self.image_byte_array)
3735
self.check_save("some_uuid")
3836

3937
@pytest.mark.parametrize("exists", [True, False])
@@ -42,17 +40,13 @@ def test_exists(self, exists):
4240
os.makedirs(os.path.join(self.upload_folder, "some_uuid"))
4341
assert self.manager.uuid_exists(["some_uuid"]) == exists
4442

45-
def test_read_data(self):
46-
os.makedirs(os.path.join(self.upload_folder, "some_uuid"))
47-
with open(os.path.join(self.upload_folder, "some_uuid", "data"), "w") as f:
48-
f.write('{"foo": "bar"}')
49-
assert self.manager._read_data(["some_uuid"]) == {"foo": "bar"}
50-
51-
def test_read_file(self):
43+
def test_get_image(self):
5244
os.makedirs(os.path.join(self.upload_folder, "some_uuid"))
5345
with open(os.path.join(self.upload_folder, "some_uuid", "file"), "wb") as f:
54-
f.write(b"READ DATA")
55-
assert self.manager._read_file(["some_uuid"]) == b"READ DATA"
46+
f.write(self.image_byte_array)
47+
result_image = self.manager.get_image(["some_uuid"])
48+
diff = ImageChops.difference(result_image.convert("RGB"), self.image)
49+
assert diff.getbbox() is None
5650

5751
def test_path_for_uuid_with_sep(self):
5852
path = self.manager.path_for_uuid(["foo", "bar"])

tests/views/test_images.py

+10-9
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
# -*- coding: utf-8 -*-
22
import base64
3-
import json
3+
import io
44
import os
55

66
import pytest
77
import pytest_asyncio
8+
from PIL import Image
89

910

1011
@pytest.fixture()
@@ -63,28 +64,28 @@ async def test_ok(self, filename):
6364
assert os.path.isdir(os.path.join(self.upload_folder, "test_client", expected_filename))
6465
with open(os.path.join(self.upload_folder, "test_client", expected_filename, "file"), "rb") as file:
6566
assert file.read() == b"abcdef"
66-
with open(os.path.join(self.upload_folder, "test_client", expected_filename, "data"), "r") as file:
67-
assert json.loads(file.read()) == dict(
68-
mimetype="image/jpeg",
69-
)
7067

7168

7269
@pytest.mark.asyncio
7370
class TestImageViewGet:
7471
@pytest_asyncio.fixture(autouse=True)
7572
def _setup(self, client, upload_folder):
7673
self.client = client
74+
75+
image = Image.new(mode="RGB", size=(3, 3))
76+
img_stream = io.BytesIO()
77+
image.save(img_stream, format="jpeg")
78+
self.image_byte_array = img_stream.getvalue()
79+
7780
os.makedirs(os.path.join(upload_folder, "some_client_id", "some_uuid"))
7881
with open(os.path.join(upload_folder, "some_client_id", "some_uuid", "file"), "wb") as f:
79-
f.write(b"abcdef")
80-
with open(os.path.join(upload_folder, "some_client_id", "some_uuid", "data"), "w") as f:
81-
f.write(json.dumps(dict(mimetype="image/jpeg")))
82+
f.write(self.image_byte_array)
8283

8384
async def test_ok(self):
8485
response = await self.client.get("/v1/image/some_client_id/some_uuid")
8586
assert response.status_code == 200
8687
assert response.headers["Content-Type"] == "image/jpeg"
87-
assert response.content == b"abcdef"
88+
assert response.content == self.image_byte_array
8889

8990
async def test_no_client_id(self):
9091
response = await self.client.get("/v1/image/some_uuid")

0 commit comments

Comments
 (0)