Skip to content

Commit edc624f

Browse files
authored
Add delete methods to storages and fields (#53)
1 parent b794da2 commit edc624f

File tree

6 files changed

+71
-10
lines changed

6 files changed

+71
-10
lines changed

fastapi_storages/base.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ def open(self, name: str) -> BinaryIO:
2121
def write(self, file: BinaryIO, name: str) -> str:
2222
raise NotImplementedError()
2323

24+
def delete(self, name: str) -> None:
25+
raise NotImplementedError()
26+
2427
def generate_new_filename(self, filename: str) -> str:
2528
raise NotImplementedError()
2629

@@ -72,6 +75,13 @@ def write(self, file: BinaryIO) -> str:
7275

7376
return self._storage.write(file=file, name=self._name)
7477

78+
def delete(self) -> None:
79+
"""
80+
Delete file from the storage
81+
"""
82+
83+
return self._storage.delete(self._name)
84+
7585
def __str__(self) -> str:
7686
return self.path
7787

fastapi_storages/filesystem.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,13 @@ def write(self, file: BinaryIO, name: str) -> str:
6464

6565
return str(path)
6666

67+
def delete(self, name: str) -> None:
68+
"""
69+
Delete the file from the filesystem.
70+
"""
71+
72+
Path(self.get_path(name)).unlink()
73+
6774
def generate_new_filename(self, filename: str) -> str:
6875
counter = 0
6976
path = self._path / filename

fastapi_storages/s3.py

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -54,14 +54,13 @@ def __init__(self) -> None:
5454

5555
self._http_scheme = "https" if self.AWS_S3_USE_SSL else "http"
5656
self._url = f"{self._http_scheme}://{self.AWS_S3_ENDPOINT_URL}"
57-
self._s3 = boto3.resource(
57+
self._s3 = boto3.client(
5858
"s3",
5959
endpoint_url=self._url,
6060
use_ssl=self.AWS_S3_USE_SSL,
6161
aws_access_key_id=self.AWS_ACCESS_KEY_ID,
6262
aws_secret_access_key=self.AWS_SECRET_ACCESS_KEY,
6363
)
64-
self._bucket = self._s3.Bucket(name=self.AWS_S3_BUCKET_NAME)
6564

6665
def get_name(self, name: str) -> str:
6766
"""
@@ -86,10 +85,8 @@ def get_path(self, name: str) -> str:
8685
)
8786

8887
if self.AWS_QUERYSTRING_AUTH:
89-
params = {"Bucket": self._bucket.name, "Key": key}
90-
return self._s3.meta.client.generate_presigned_url(
91-
"get_object", Params=params
92-
)
88+
params = {"Bucket": self.AWS_S3_BUCKET_NAME, "Key": key}
89+
return self._s3.generate_presigned_url("get_object", Params=params)
9390

9491
return "{}://{}/{}/{}".format(
9592
self._http_scheme,
@@ -104,7 +101,9 @@ def get_size(self, name: str) -> int:
104101
"""
105102

106103
key = self.get_name(name)
107-
return self._bucket.Object(key).content_length
104+
return self._s3.head_object(Bucket=self.AWS_S3_BUCKET_NAME, Key=key)[
105+
"ContentLength"
106+
]
108107

109108
def write(self, file: BinaryIO, name: str) -> str:
110109
"""
@@ -118,9 +117,16 @@ def write(self, file: BinaryIO, name: str) -> str:
118117
"ACL": self.AWS_DEFAULT_ACL,
119118
"ContentType": content_type or self.default_content_type,
120119
}
121-
self._bucket.upload_fileobj(file, key, ExtraArgs=params)
120+
self._s3.upload_fileobj(file, self.AWS_S3_BUCKET_NAME, key, ExtraArgs=params)
122121
return key
123122

123+
def delete(self, name: str) -> None:
124+
"""
125+
Delete the file from S3
126+
"""
127+
128+
self._s3.delete_object(Bucket=self.AWS_S3_BUCKET_NAME, Key=self.get_name(name))
129+
124130
def generate_new_filename(self, filename: str) -> str:
125131
key = self.get_name(filename)
126132
stem = Path(filename).stem
@@ -136,7 +142,7 @@ def generate_new_filename(self, filename: str) -> str:
136142

137143
def _check_object_exists(self, key: str) -> bool:
138144
try:
139-
self._bucket.Object(key).load()
145+
self._s3.head_object(Bucket=self.AWS_S3_BUCKET_NAME, Key=key)
140146
except boto3.exceptions.botocore.exceptions.ClientError as e:
141147
if e.response["Error"]["Code"] == "404":
142148
return False

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ test = "coverage run -m pytest {args}"
9191
dependencies = [
9292
"mkdocs-material==9.5.2",
9393
"mkdocs==1.5.3",
94-
"mkdocstrings[python]==0.24.0",
94+
"mkdocstrings[python]==0.25.0",
9595
]
9696

9797
[tool.hatch.envs.docs.scripts]

tests/test_filesystem_storage.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,3 +64,18 @@ class NonOverwritingFileSystemStorage(FileSystemStorage):
6464
assert Path(file1.path) == tmp_path / "duplicate.txt"
6565
assert Path(file2.path) == tmp_path / "duplicate_1.txt"
6666
assert Path(file3.path) == tmp_path / "duplicate_2.txt"
67+
68+
69+
def test_filesystem_storage_delete_file(tmp_path: Path) -> None:
70+
input_file = tmp_path / "input.txt"
71+
input_file.write_bytes(b"123")
72+
73+
storage = FileSystemStorage(path=tmp_path)
74+
file = StorageFile(name="example.txt", storage=storage)
75+
file.write(file=input_file.open("rb"))
76+
77+
assert (tmp_path / "example.txt").exists() is True
78+
79+
file.delete()
80+
81+
assert (tmp_path / "example.txt").exists() is False

tests/test_s3_storage.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
from pathlib import Path
33

44
import boto3
5+
import pytest
6+
from botocore.exceptions import ClientError
57
from moto import mock_s3
68

79
from fastapi_storages import S3Storage, StorageFile
@@ -111,3 +113,24 @@ class TestStorage(PrivateS3Storage):
111113
assert file1.path == "http://s3.fastapi.storages/duplicate.txt"
112114
assert file2.path == "http://s3.fastapi.storages/duplicate_1.txt"
113115
assert file3.path == "http://s3.fastapi.storages/duplicate_2.txt"
116+
117+
118+
@mock_s3
119+
def test_s3_storage_delete_file(tmp_path: Path) -> None:
120+
s3 = boto3.client("s3")
121+
s3.create_bucket(Bucket="bucket")
122+
123+
tmp_file = tmp_path / "example.txt"
124+
tmp_file.write_bytes(b"123")
125+
126+
storage = PrivateS3Storage()
127+
128+
file = StorageFile(name="file.txt", storage=storage)
129+
file.write(file=tmp_file.open("rb"))
130+
131+
assert s3.head_object(Bucket="bucket", Key="file.txt")
132+
133+
file.delete()
134+
135+
with pytest.raises(ClientError):
136+
s3.head_object(Bucket="bucket", Key="file.txt")

0 commit comments

Comments
 (0)