チケット #44 (new blog)

登録: 2 年

最終更新: 20 か月

pythonでメモリ上にzipを作る。

報告者: mtamaki 担当者: mtamaki
優先度: major マイルストーン:
コンポーネント: blog バージョン:
キーワード: 関係者:

説明 (最終更新者: mtamaki) (diff)

昨日の続き。メモリ上にzip形式のファイルオブジェクトを作る関数。 zipしたいファイルのリストかフォルダパスを指定するだけだから使いやすい。。。かな?

def zip_create_obj( path_or_paths, base_path = None ):
    "base_pathからの相対パスとしてファイルを格納したzip形式のファイルオブジェクトを返す。"
    def path_get_files( path ):
        import stat, os, fnmatch
        if not ( os.access( path, os.F_OK ) and stat.S_ISDIR( os.stat( path )[stat.ST_MODE] ) ):
            #ファイル名部分を抜き出す。
            file_name_filter = os.path.split(path)[1]
            path = os.path.dirname( path )
        else:
            file_name_filter = "*"

        files = os.listdir(path)
        results = []
        for file in files:
            child_path = os.path.join(path, file)
            #ディレクトリではなく、
            if not ( os.access( child_path, os.F_OK ) and stat.S_ISDIR( os.stat( child_path )[stat.ST_MODE] ) ):
                #フィルタにマッチするとき
                if fnmatch.fnmatch( os.path.split(path)[1], file_name_filter ):
                    #追加
                    results.append(child_path)
        return results

    def path_get_folders(path):
        import os, stat
        results = []
        try:
            files = os.listdir(path)
            for file in files:
                child_path = os.path.join(path, file)
                if os.access( child_path, os.F_OK ) and stat.S_ISDIR( os.stat( child_path )[stat.ST_MODE] ):
                    #追加
                    results.append(child_path)
        except WindowsError:
            pass
        return results

    def path_get_recursive_files( path ):
        import stat, os
        #フォルダを指定していたら最後に*をつける。
        if os.access( path, os.F_OK ) and stat.S_ISDIR( os.stat( path )[stat.ST_MODE] ):
            path += "/*"
        #このフォルダのファイルを取得
        results = path_get_files( path )
        #サブフォルダを取得
        for sub_folder in path_get_folders( os.path.dirname(path) ):
            results += path_get_recursive_files( sub_folder + "/" + os.path.split(path)[1] )
        return results
    def zip_raw_write(zip_file, zinfo_or_arcname, obj):
        #base: zipfile.ZipFile.writestr
        from zipfile import ZipInfo, ZIP_DEFLATED
        import time
        if not isinstance(zinfo_or_arcname, ZipInfo):
            zinfo = ZipInfo(filename=zinfo_or_arcname,
                            date_time=time.localtime(time.time()))
            zinfo.compress_type = zip_file.compression
        else:
            zinfo = zinfo_or_arcname
        zip_file._writecheck(zinfo)
        read_obj = obj.read()
        zinfo.file_size = obj.tell()            # Uncompressed size
        import binascii
        zinfo.CRC = binascii.crc32(read_obj)       # CRC-32 checksum
        if zinfo.compress_type == ZIP_DEFLATED:
            import zlib
            co = zlib.compressobj(zlib.Z_DEFAULT_COMPRESSION,
                 zlib.DEFLATED, -15)
            read_obj = co.compress(read_obj) + co.flush()
            zinfo.compress_size = len(read_obj)    # Compressed size
        else:
            zinfo.compress_size = zinfo.file_size
        zinfo.header_offset = zip_file.fp.tell()    # Start of header obj
        zip_file.fp.write(zinfo.FileHeader())
        zinfo.file_offset = zip_file.fp.tell()      # Start of file obj
        zip_file.fp.write(read_obj)
        if zinfo.flag_bits & 0x08:
            # Write CRC and file sizes after the file data
            zip_file.fp.write(struct.pack("<lLL", zinfo.CRC, zinfo.compress_size,
                  zinfo.file_size))
        zip_file.filelist.append(zinfo)
        zip_file.NameToInfo[zinfo.filename] = zinfo

    import os, sys, stat
    #まず、第一引数が単一のファイルかファイルのリストかを調べる
    if isinstance( path_or_paths, (list, tuple) ):
        zip_files = path_or_paths
    elif os.access( path_or_paths, os.F_OK ) and stat.S_ISDIR( os.stat( path_or_paths )[stat.ST_MODE] ):
        #zipするファイルをリストアップ
        zip_files = path_get_recursive_files(path_or_paths)
    else:
        #zipするファイルをリストアップ
        zip_files = [path_or_paths]
    #ベースフォルダを決定
    if not base_path:
        base_path = os.path.commonprefix(zip_files)
    #末尾に\\をつける。
    if os.path.isdir(base_path):
        base_path = os.path.join(base_path, "1")[:-1]
    #zipファイルを作る。
    import zipfile, StringIO
    zip_file_obj = StringIO.StringIO()
    try:
        # zlib がない場合,この呼び出しは失敗して RuntimeError が返る
        zip_file = zipfile.ZipFile(zip_file_obj, "w", zipfile.ZIP_DEFLATED )
    except RuntimeError:
        # ZIP_STORED (非圧縮) がデフォルトの設定
        zip_file = zipfile.ZipFile(zip_file_obj, "w") #, zipfile.ZIP_STORED)
    #ファイルをzipに追加
    encoding = sys.getfilesystemencoding()
    for file in zip_files:
        arc_name = file[len(base_path):]
        if isinstance( arc_name, unicode ):
            arc_name = arc_name.encode( encoding )
        zip_file.write( file, arc_name )
    zip_file.close()
    zip_file_obj.seek(0,0)
    return zip_file_obj

if __name__ == '__main__':
        zip_obj = zip_create_obj( r"C:\WINDOWS\system32\drivers\etc" )
        import os
        zip_file_obj = open(os.path.splitext(__file__)[0] + ".zip", "wb")
        zip_file_obj.write( zip_obj.read() )
        zip_file_obj.close()

サンプルでetcをzipしてるのはわりと誰のPCにでもあって程よい大きさのフォルダだから。。。バイナリファイルが含まれてればベストなんだけどなー。

(080620追記:Python2.5でzip_raw_writeがエラーになります。 PythonZipOnMemory にあるzip_raw_writeと差し替えてください。)

チケットの履歴

更新者: mtamaki (21 か月 前)

  • 説明 が変更されました (diff)

更新者: mtamaki (20 か月 前)

  • 説明 が変更されました (diff)

更新者: mtamaki (20 か月 前)

  • 説明 が変更されました (diff)
Note: チケットについてのヘルプは TracTickets を参照 して下さい。