1. 指定されたデータサイズでファイルを分割する
指定されたファイルを指定されたデータサイズ(チャンクサイズ)で分割して、同じディレクトリに元ファイル名.0、元ファイル名.1、元ファイル名.2
という名前で出力します。
処理のポイントは元ファイルをチャンクサイズづつ読み込み、読み取り位置をシークさせていくことです。チャンクサイズしかメモリに展開しないため、巨大なファイルでも分割することが可能です。
(注意)
サンプルではチャンクサイズは最大でも数MBを想定しています。数十~数百MBでファイルを分割したい場合、一度でチャンクサイズのデータを読み込むのではなく、バッファリングしつつ読み込んで分割ファイルに書き込むようにしてください。
# 指定されたデータサイズでファイルを分割する
def divide_file(filePath, chunkSize):
readedDataSize = 0
i = 0
fileList = []
# 対象ファイルを開く
f = open(filePath, "rb")
# ファイルを読み終わるまで繰り返す
contentLength = os.path.getsize(filePath)
while readedDataSize < contentLength:
# 読み取り位置をシーク
f.seek(readedDataSize)
# 指定されたデータサイズだけ読み込む
data = f.read(chunkSize)
# 分割ファイルを保存
saveFilePath = filePath + "." + str(i)
with open(saveFilePath, 'wb') as saveFile:
saveFile.write(data)
# 読み込んだデータサイズの更新
readedDataSize = readedDataSize + len(data)
i = i + 1
fileList.append(saveFilePath)
return fileList
2. ファイルを結合する
結合したいファイルのファイルパスが格納されたリストに従い、指定されたファイルパスに結合したファイルを出力します。
処理のポイントは結合ファイル毎にフラッシュしてファイルに書き込むことです。
注意点は分割と同じで、分割ファイルのデータサイズが大きい場合、バッファリングしながら読み込んでファイルに書き込むことです。
# 渡されたファイルリストの順序で1つのファイルに結合する
def join_file(fileList, filePath):
with open(filePath, 'wb') as saveFile:
for f in fileList:
data = open(f, "rb").read()
saveFile.write(data)
saveFile.flush()
3. 指定された部分データをファイルから取得する
指定されたファイルから指定された範囲の部分データ(start、end自体を含む)を取得します。
もともと今回の記事はflaskでRange Requestsによるファイルダウンロードを検討したことが始まりです。その際に必要となる指定範囲のデータをファイルから取得する処理のお試し実装になります。
ポイントはファイル分割と同じで読み取り位置をシークさせ、必要なデータだけを取得するところになります。
# 指定された部分データをファイルから取得する
def partial_content(filePath, start, end):
partialSize = end - start + 1
f = open(filePath, "rb")
f.seek(start)
data = f.read(partialSize)
return data
4. テスト
手元にあった画像ファイル(jpeg)を今回実装した処理で分割、結合して正しく表示できるか確認しました。
# main
if __name__ == "__main__":
# 3126866 Byte
target = "sample.jpg"
# 250000 Byte で分割
fileList = divide_file(target, 250000)
# 分割したファイルを結合
join_file(fileList, 'join_' + target
# 0-9999 の 10000 Byte を取り出す
data01 = partial_content(target, 0, 9999)
# 10000-残り全て を取り出す
data02 = partial_content(target, 10000, os.path.getsize(target))
# 結合して確かめる
with open('partial_' + target, 'wb') as filePartial:
filePartial.write(data01)
filePartial.write(data02)