CakePHP使いがDjangoでサイトを作ってみた 〜ファイルのダウンロード(合法)〜

この記事はだいぶ前に書かれたものなので情報が古いかもしれません
最初はロック

この記事を三行にまとめると

URLでアクセスできるファイルをダウンロード
URLでアクセスできないファイルをダウンロード
CSVを作成してファイルとしてダウンロード
さーて、今週のあかつきのお宿は……

・URLでアクセスできるファイルをダウンロード
・URLでアクセスできないファイルをダウンロード
・CSVを作成してファイルとしてダウンロード

の三本です。それじゃあさっそく行ってみましょう。さーいしょーはロック……ジャン、ケン、グーッ!!(CV:ゴンさん)




URLでアクセスできるファイルをダウンロード

まずはURLでアクセスできるファイルのダウンロードです。ブログで表示している画像などがこれに該当しますね。たぶん一番簡単なパターンだと思います。

#views.py
from django.views.generic import TemplateView
from django.http import HttpResponse
import requests

class download(TemplateView) :
  def get(self, request, *args, **kwargs) :
    image = requests.get('https://norm-nois.com/static/images/sample.jpg')
    response = HttpResponse(image.content, content_type = 'image/jpeg')
    response['Content-Disposition'] = 'attachment; filename=サンプル画像.jpg'
    return response

URLでアクセスできる場合は「requests.get()」を使います。phpの「file_get_contens()」やcurlなんかと同じような機能です。もちろん画像以外のファイルのダウンロードにも使えますし、APIの開発なんかでも重宝する機能です。requestsがサーバーにない場合はpipコマンドでインストールできます。

$ pip install requests

データを取得できたらあとはそれをHttpResponse()に突っ込んでやるだけです。



URLでアクセスできないファイルをダウンロード

URLでアクセスできるってことは誰にでも見えちゃうという意味なので、セキュリティ的にはガバガバです。個人情報が書かれたファイルなどを置く時は基本的にURLでアクセスできない場所に置いておいた方が良いでしょう。

そういうファイルをダウンロードする時はどうするか。

#views.py
from django.views.generic import TemplateView
from django.http import HttpResponse

class download(TemplateView) :
  def get(self, request, *args, **kwargs) :
    file = open('/var/images/sample.jpg', 'rb')
    response = HttpResponse(file, content_type = 'image/jpeg')
    response['Content-Disposition'] = 'attachment; filename=サンプル画像.jpg'
    return response

URLにアクセスできないところにあるファイルのデータを取得する場合は「open()」というファイルを読み込む組み込み関数を使います。open()でファイルを開くことができたらあとはURLでアクセスできるファイルと流れは同じです。

open()以外にもストリームを扱うioモジュールのFileIO()を使っても同じことができます。ストリームってのは……何だ? まあデータの一種だと思っとけば良いでしょう。

#views.py
from django.views.generic import TemplateView
from django.http import HttpResponse
import io

class download(TemplateView) :
  def get(self, request, *args, **kwargs) :
    file = io.FileIO('/var/images/sample.jpg')
    response = HttpResponse(file.readall(), content_type = 'image/jpeg')
    response['Content-Disposition'] = 'attachment; filename=サンプル画像.jpg'
    return response

やってることは同じですがioモジュールを使った場合は「readall()」という関数でストリームの全バイトを読み込んでHttpResponse()に渡す必要があります。



CSVを作成してファイルとしてダウンロード

最後はサーバー上にファイルがない状態でデータをCSVファイルに落とし込んでダウンロードしてみたいと思います。

#views.py
from django.views.generic import TemplateView
from django.http import HttpResponse
import io, csv

class download(TemplateView) :
  def get(self, request, *args, **kwargs) :
    sio = io.StringIO()
    writer = csv.writer(sio)
    writer.writerow(['A1', 'B1', 'C1'])
    writer.writerow(['A2', 'B2', 'C2'])
    writer.writerow(['A3', 'B3', 'C3'])

    response = HttpResponse(content_type = 'text/csv')
    response['Content-Disposition'] = 'attachment; filename=リスト.csv'
    response.write(sio.getvalue().encode('cp932'))
    return response

ここでもioモジュールが活躍します。サーバーにファイルを保存する場合はopen()関数を使えば良いと思うんですが、ファイルを残さずダウンロードだけ行う場合はこっちの方が良いでしょう。

Django……というかPythonにはcsvというCSVを扱うためのモジュールが存在するので、それを使ってデータの書き込みを行い、HttpResponse()でファイルのダウンロード処理を行なっています。

最後の「sio.getvalue().encode(‘cp932’)」ってやつは書き込んだ値の文字コードを変換する処理です。文字コードがUTF-8の場合、CSVファイルをExcelで開くと文字化けしてしまうことがあるのでSHIFT-JISに変換しておく必要があります。cp932というのはSHIFT-JISの一種なのですが、SHIFT-JISよりも扱える文字が多いです。

ちなみにCSVじゃなくてもioモジュールを使えば通常のテキストファイルなども作成してダウンロードすることが可能です。

sio = io.StringIO()
sio.write('テキストアフィルです')
response = HttpResponse(sio.getvalue(), content_type = 'plain/text')
response['Content-Disposition'] = 'attachment; filename=sample.txt'
return response






ファイルの読み込み方、あるいは書き込み方に差があるだけで、HttpResponse()を使ってダウンロードを行う点はどれも同じです。なのでとりあえずこれだけやり方を覚えておけば他のパターンの場合もこの応用でいけると思います。

ところでダウンロードと言えば、最近5G回線に関する情報をちょくちょく目にすることがあります。噂じゃ映画一本分くらいの動画ファイルでも数秒でダウンロードできるらしいんですけど……たかが数MBのBM98の楽曲データをダウンロードするのに一晩かかってた頃を思うと、超スピードとかそんなチャチなレベルじゃねえな、これ。そのうちダウンロードしようと頭の中で思った時にはすでにダウンロードが終わってる時代が来るのか?
 もしかしたら何か関連しているかも? 
 質問や感想などお気軽にコメントしてください