pythonでzipファイルをダウンロードさせることにハマる

| | Comments (0) | TrackBacks (0)
HDEラボの桜井です。
3月寒いっす。

なんとなく、困ったことがあります。
pythonでzipファイルをWebブラウザ経由でダウンロードさせるプログラムを作り、
さらにそのダウンロードじたzipファイルを再びアップロードするプログラムを作ったところ、
なんと、アップロード時に正しいzipファイルと認識されなかったのである。

問題のプログラムを見てみよう。
test.cgi
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import zipfile, cgi, os, shutil
import web.template

form = cgi.FieldStorage()
command = form.getfirst('command', '')

message = ""
error = 0

if command == "backup":

    zip = zipfile.ZipFile("/tmp/test.zip", 'w', zipfile.ZIP_DEFLATED)
    zip.write("/tmp/file1", "file1")
    zip.close()

    print "Content-Type: application/zip;\r\nContent-Disposition: attachment; filename=test.zip\r\n\r\n",
    f = open("/tmp/test.zip")
    print f.read(),
    f.close()

    os.unlink("/tmp/test.zip")

else:

    if command == "restore":

        if form.has_key('zip'):
            item = form['zip']
            if item.file:
                try:
                    tmpFileObj = file("/tmp/test2.zip", 'wb')
                    shutil.copyfileobj(item.file, tmpFileObj)
                    tmpFileObj.close()
                    os.chmod("/tmp/test2.zip", 0777)
                except Exception, ex:
                    error = 1
                    message = "ファイルのアップロードに失敗しました"
            else:
                error = 1
                message = "ファイルが選択されていません"
        else:
            error = 1
            message = "ファイルが選択されていません"

        if error == 0 and zipfile.is_zipfile("/tmp/test2.zip"):
            zip = zipfile.ZipFile("/tmp/test2.zip")
            for name in zip.namelist():
                f = open("/tmp/" + name, 'w')
                f.write(zip.read(name))
                f.close()
            zip.close()
                os.unlink(tmpPath + file)

            message = "復元しました"

        else:
            error = 1
            message = "復元できませんでした"

        os.unlink("/tmp/test2.zip")

    values = { "command" : command,
               "message" : message,
               "error"   : error
             }

    render = web.template.render("./template")
    print 'Content-Type: text/html; charset=utf-8'
    print ''
    print render.test(values)


それから、テンプレートファイル(テンプレートの詳細はこちら
./template/test.html
$def with (values)
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" id="sixapart-standard">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>zipダウンロードとアップロード</title>
</head>
<body>

<h2>zipダウンロード</h2>

$if values["command"] == "backup":
  $if values["error"] == 1:
    <h3><font color="red">$values["message"]</font></h3>
  $else:
    <h3>$values["message"]</h3>

<form action="backup.cgi" method="POST">
<input type="hidden" name="command" value="backup">
<input type="submit" value="設定ファイルをバックアップする">
</form>

<hr>

<h2>zipアップロード</h2>

$if values["command"] == "restore":
  $if values["error"] == 1:
    <h3><font color="red">$values["message"]</font></h3>
  $else:
    <h3>$values["message"]</h3>

<form action="backup.cgi" method="POST" ENCTYPE="multipart/form-data">
<input type="hidden" name="command" value="restore">
バックアップファイル(ZIP形式):<input type="file" name="zip"><br/>
<input type="submit" value="設定ファイルを復元する">
</form>

</body></html>




こうするとどうなるかというと、ダウンロードした際にWebブラウザがzipファイルの末尾に改行コードを付けて保存するのです。
また、pythonのzipfileモジュールは、厳密にzipファイルをチェックしているようで、この改行コードがあるだけでzipファイルだとはみなしてくれません。
よって、アップロード時にはエラーになり「復元できません」の表示が出るのです。

解決策をいろいろ考えましたが、ダウンロード時にWebブラウザが改行を付加しない方法が思いつかなかったので、アップロード時に末尾の改行を1バイト削る方向で対応してみました。
下記のコードです。
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import zipfile, cgi, os, shutil
import web.template

form = cgi.FieldStorage()
command = form.getfirst('command', '')

message = ""
error = 0

if command == "backup":

    zip = zipfile.ZipFile("/tmp/test.zip", 'w', zipfile.ZIP_DEFLATED)
    zip.write("/tmp/file1", "file1")
    zip.close()

    print "Content-Type: application/zip;\r\nContent-Disposition: attachment; filename=test.zip\r\n\r\n",
    f = open("/tmp/test.zip")
    print f.read(),
    f.close()

    os.unlink("/tmp/test.zip")

else:

    if command == "restore":

        if form.has_key('zip'):
            item = form['zip']
            if item.file:
                try:
                    tmpFileObj = file("/tmp/test2.zip", 'wb')
                    shutil.copyfileobj(item.file, tmpFileObj)
                    tmpFileObj.close()

                    # めっちゃ泥臭いですが、強引に改行を削ります
                    tmpFileObj2 = open("/tmp/test2.zip", 'r')
                    tmpFileStr = tmpFileObj2.read()
                    tmpFileObj2.close()
                    tmpFileObj3 = open("/tmp/test2.zip", 'w')
                    tmpFileObj3.write(tmpFileStr.rstrip())
                    tmpFileObj3.close()
 
                   os.chmod("/tmp/test2.zip", 0777)
                except Exception, ex:
                    error = 1
                    message = "ファイルのアップロードに失敗しました"
            else:
                error = 1
                message = "ファイルが選択されていません"
        else:
            error = 1
            message = "ファイルが選択されていません"

        if error == 0 and zipfile.is_zipfile("/tmp/test2.zip"):
            zip = zipfile.ZipFile("/tmp/test2.zip")
            for name in zip.namelist():
                f = open("/tmp/" + name, 'w')
                f.write(zip.read(name))
                f.close()
            zip.close()
                os.unlink("/tmp/test2.zip")

            message = "復元しました"

        else:
            error = 1
            message = "復元できませんでした"

        os.unlink(tmpPath + tmpFile)

    values = { "command" : command,
               "message" : message,
               "error"   : error
             }

    render = web.template.render("./template")
    print 'Content-Type: text/html; charset=utf-8'
    print ''
    print render.test(values)


もっと素敵な方法があったら知りたいもんです、マジで。

0 TrackBacks

Listed below are links to blogs that reference this entry: pythonでzipファイルをダウンロードさせることにハマる.

TrackBack URL for this entry: http://lab.hde.co.jp/blog/mt-tb.cgi/103

Leave a comment

About this Entry

This page contains a single entry by Takeshi SAKURAI published on March 12, 2009 5:58 PM.

ガラナ アンタルチカを飲んでみた was the previous entry in this blog.

pythonでダウンロードCGIにハマった件の解決 is the next entry in this blog.

Find recent content on the main index or look in the archives to find all content.