0
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

pythonちょい嵌り-NamedTemporaryFile-

Last updated at Posted at 2019-02-02

はじめに

pythonで一時的にファイルをつくり、CSVファイル書き出し
それをHTTPレスポンスでZIPした状態で返すコードを書いている最中で
UnsupportedOperation: not readableが出て困っていました。
という訳で、ちょい嵌りしたポイントをご紹介します。

pythonで一時ファイルを作成する

11.6. tempfile — 一時ファイルやディレクトリの作成」のサンプルを参考にすれば一時ファイルはすぐに作れます。
しかもcloseすると勝手に消してくれる優れもの(^^♪

import tempfile
fp = tempfile.TemporaryFile()
fp.write(b'Hello world!')
fp.seek(0)
fp.read()
fp.close()

こいつは優れものだなぁ~と思い次の様に流用しました。

CSVファイルを一時ファイルに作成しZIPでレスポンスを返す(失敗)

contentに辞書型でデータ列が入っていると仮定してみてください。
するとCSV出力するのはこんな感じになると思います。

import csv
import tempfile

fp = tempfile.NamedTemporaryFile(mode='w', newline='', encoding='shift_jis')
fname = fp.name
header=[]
for key in content:
    header.append(key)
writer = csv.DictWriter(fp, fieldnames=header)
writer.writeheader()
writer.writerows(content)

出来上がったCSVファイルをZIP圧縮してHTTPレスポンスに返すには…
このファイルをPythonのマニュアルの例通りseek(0)してread()すれば値が取れるかなと思っていたのですが…

import os
import io
import csv
import tempfile
import zipfile
from django.http import HttpResponse

fp = tempfile.NamedTemporaryFile(mode='w', newline='', encoding='shift_jis', delete=False)
fname = fp.name
header=[]
for key in content:
    header.append(key)
writer = csv.DictWriter(fp, fieldnames=header)
writer.writeheader()
writer.writerows(content)

fp.seek(0)

zip_io = io.BytesIO()
 with zipfile.ZipFile(zip_io, mode='w', compression=zipfile.ZIP_DEFLATED) as backup_zip:
    backup_zip.writestr('CSVファイル.csv', fp.read())
response = HttpResponse(zip_io.getvalue(), content_type='application/x-zip-compressed')
response['Content-Disposition'] = 'attachment; filename=%s' % 'your_zipfilename' + ".zip"
response['Content-Length'] = zip_io.tell()
fp.close()
return response

こんな感じかなと実行してみると…
fp.read()の段階でUnsupportedOperation: not readableの例外が発生
色々ネットを徘徊しましたが、どうやら'w'モードで開いた状態でread()は出来ないらしいことが判明

どうする俺(笑)

CSVファイルを一時ファイルに作成しZIPでレスポンスを返す(成功)

結局一時ファイルを自動的に消してくれるclose()の恩恵を無視し(delete=False)
一時ファイルに書き込みclose()し、読み取りモードで開いて読み込みHTTPレスポンスへ流し、最後に消すって流れになってしまいました。

import os
import io
import csv
import tempfile
import zipfile
from django.http import HttpResponse

fp = tempfile.NamedTemporaryFile(mode='w', newline='', encoding='shift_jis', delete=False)
fname = fp.name
header=[]
for key in content:
    header.append(key)
writer = csv.DictWriter(fp, fieldnames=header)
writer.writeheader()
writer.writerows(content)
fp.close()

fp = open(fname, 'r')
fp.seek(0)    # これはいらないかもね

zip_io = io.BytesIO()
 with zipfile.ZipFile(zip_io, mode='w', compression=zipfile.ZIP_DEFLATED) as backup_zip:
    backup_zip.writestr('CSVファイル.csv', fp.read())
response = HttpResponse(zip_io.getvalue(), content_type='application/x-zip-compressed')
response['Content-Disposition'] = 'attachment; filename=%s' % 'your_zipfilename' + ".zip"
response['Content-Length'] = zip_io.tell()
fp.close()
os.unlink(fname)
return response

本当は

一時ファイルすら作りたくなくって、直接ZIP化できればよいんですけどね。

0
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?