FileReader関数を利用して分割アップロードする
アップロード前に画像のプレビューを表示するくらいしか使い道が思いつかなかったFileReader関数ですが、実用的な使途がありました。
サーバーの制限を回避する
php.iniなど設定を変更できないサーバーを使っている場合、タイムアウトやメモリ-不足などでアップロードが失敗してしまいます。
<?php ini_set('upload_max_filesize', '128M'); ini_set('max_execution_time', '120'); ?>
などの記述で回避できる場合もありますが、これらを書いても効かない場合もあります。ならば分割して少しずつ送信すれば良いわけです。
アップロードがメインになるようなサービスを作ろうというわけではないので、極力シンプルに書いてみたのが、以下のコードです。
ブラウザーからの分割アップロード(html部分)
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>dividing_upload</title>
<script src="./dividing_upload.js" type="text/javascript"></script>
</head>
<body>
<form>
<input id="file" name="file" type="file" />
</form>
<span id="msg"></span>
<form>
<input type="text" id="from" name="from" readonly />
<textarea id="data" name="data" readonly></textarea>
</form>
</body>
</html>
実用上は、2つめのformは非表示にした方が良いでしょうね。
ブラウザーからの分割アップロード(js部分)
var cnt=0;
var total=0;
function post(result,from,size)
{
plus=0;
data=result.substr(from,size);
if(data.substr(size-1,1)=="\r"){ // \r\nを分断すると\nを補ってくれちゃうので
plus=1;
data=result.substr(from,size+plus);
}
document.querySelector('#data').value=data;
document.querySelector('#from').value=from;
postForm('FileReaderPOST.php',document.forms[1],function(responseText){
if(responseText==''){
cnt+=data.length;
if(cnt<total){ // 2回め以降の送信
document.querySelector('#msg').innerHTML =""+cnt+"/"+total;
post(result,from+size+plus,size);
}else{ // 完了時のリセット
document.forms[0].reset();
document.forms[1].reset();
cnt=0;
total=0;
document.querySelector('#msg').innerHTML ='completed => <a target="_blank" href="FileReaderPOST.txt">download</a><br />';
}
}else{
alert(responseText);
}
});
}
function postForm(uri,formNode,callback1)
{
form = new FormData(formNode);
var xhr = new XMLHttpRequest();
xhr.open('POST', uri);
xhr.onreadystatechange=function(){
if(xhr.readyState == 4 && xhr.status == 200){
callback1(xhr.responseText);
}
};
xhr.send(form);
}
window.addEventListener('DOMContentLoaded', function() {
document.querySelector("#file").addEventListener('change', function(e) {
if (window.File) {
var input = document.querySelector('#file').files[0];
var reader = new FileReader();
reader.addEventListener('load', function(e) {
base64=btoa(reader.result); // 文字バケ防止のエンコード
post(base64,0,32768); // 初回送信、分割サイズ指定
total=base64.length;
document.querySelector('#msg').innerHTML ="送信中";
}, true);
reader.readAsBinaryString(input);
}
}, true);
});
分割をまとめるため、サーバー側で再結合する処理が必要です。
<?php
$filename='FileReaderPOST.txt'; // とりあえず固定値
if(is_file($filename)){
if($_POST['from']==0){ // 初回送信なら前回のファイルを削除
$data='';
unlink($filename);
}else{
$data=file_get_contents($filename);
}
}else{
$data='';
}
file_put_contents( // デコードしてから追加書き込み
$filename,
$data.base64_decode ($_POST['data']));
?>
file_put_contentsじゃなくてfopen("……","a")にしろ、ってところでしょうか。要するにデコードして追加書き込みをするだけなので、phpでなくても構いません。
"$filename"がハードコーディングで".txt"になっていますが、エンコードされるのでバイナリーデータも送信できます。その場合はform内にファイルタイプを追加して、拡張子を変更させれば良いでしょう。