【Django】ストレージに保存されているファイルにアクセス、編集して保存する【InMemoryUploadedFile】
アップロードされているファイルを編集したいことがある。
今回はテキストファイルを想定して、コードを書いてみた。
ただし、開発中にファイルパスを直接指定して編集する方法は、デプロイ後は使えないので、その点も考慮した。
実際にS3ストレージに保存されているファイルを読み込み、編集して保存できた。
ストレージに保存されているファイルを編集する。
手順は
- ストレージの保存先URLを取得する
- URLに対してリクエストを送る
- レスポンスデータをメモリ空間内に読み込み
- 編集する
- InMemoryUploadedFileのオブジェクトを作る
- バリデーションして保存する
となっている。
# txtファイルを追記する。
document = Document.objects.filter(id=1).first()
# URL を使ってリクエストを送らなければ読み込みできないため、requestsを使う。
# 直接読み込み書き込みは失敗する
"""
if document:
messages.info(request, document.file.url)
with open(document.file.url, 'a') as f:
f.write("追記されました\n")
"""
# ストレージサーバーに対してリクエストを送る。
response = requests.get(document.file.url)
# レスポンスをメモリ内に読み込み
file_stream = io.BytesIO(response.content)
file_stream.seek(0, io.SEEK_END)
file_stream.write(b"\naaaaa")
file_stream.seek(0)
# InMemoryUploadedFile のオブジェクトを作る
uploaded_file = InMemoryUploadedFile(
file=file_stream,
field_name="file",
name=document.file.name,
content_type="text/plain",
size=file_stream.getbuffer().nbytes,
charset=None
)
# バリデーションして保存する。
dic = {}
dic["file"] = uploaded_file
form = DocumentForm({}, dic, instance=document)
if form.is_valid():
form.save()
else:
print(form.errors)
ただこのコードの欠点は、編集前のファイルが残ってしまうこと。
だから、Django-cleanupを使う。
【django-cleanup】画像等のファイルを自動的に削除する
結論
少々回りくどいが、メモリ内に読み込みさせ、データを編集しているので、openpyxlなどでもおそらくうまく動いてくれるだろう。