とあるクラウド OCR に画像ファイルを API リクエストする際に、仕様が"multipart/form-data"形式で、更にファイル1つずつのみしか受け付けないものだったので、それに合わせ「画像をサイトにまとめてアップロードした後、ボタン押下で画像をひとつずつ順に API リクエストする」処理を作成しました。備忘録に残しておきます。。。
「input type="file"」の multiple 属性と formData オブジェクトと が鍵。
ローカルから Web サイトに複数ファイルアップロードする HTML
<input type="button" class="btn btn-primary" value="ファイル選択" onClick="start_img_select();">
<input type="file" name="image_data" multiple="multiple" accept="image/*" onChange="change_img(this)" id="image_data">
「input type="file"」の multiple 属性を"multiple"にすると複数のファイルがアップロードできるようになります。
上記 HTML では、「ファイル選択」ボタンをクリックすると、"start_img_select()"関数で「input type="file"」をクリックしたことにし、ファイル選択ウィンドウを開くようにしています。
「input type="file"」を使用するとファイル選択のウィンドウがデフォルトで出てくるので楽です。
JavaScript 処理
let ocrcount = 0;
let fileList = [];
let fileCount = 0;
// 「ファイル選択」ボタン押下時
function start_img_select() {
$("#image_data").css("display", "");
$("#image_data").trigger('click');
}
// ファイルアップロード後処理
function change_img(e) {
ocrcount = 0;
fileList = e.files;
fileCount = e.files.length;
let fileName = fileList[ocrcount].name;
// ファイル読み込みでプレビュー表示
var reader = new FileReader();
reader.onload = function (e) {
$("#preview").attr('src', e.target.result);
}
reader.readAsDataURL(fileList[ocrcount]);
$("#image_data").css("display", "none");
let fileinfo = `読み込みファイル数: ${ocrcount + 1}/${fileCount}<br>${fileName}`;
document.getElementById('fileinfo').innerHTML = fileinfo;
// ファイル送信実行部
call_execute_form(fileList[ocrcount]);
}
// リクエスト完了後に表示される「次へ」ボタン押下時
function nextOcr() {
ocrcount = ocrcount + 1;
let fileName = fileList[ocrcount].name;
// ファイル読み込みでプレビュー表示
var reader = new FileReader();
reader.onload = function (e) {
$("#preview").attr('src', e.target.result);
}
reader.readAsDataURL(fileList[ocrcount]);
$("#image_data").css("display", "none");
let fileinfo = `読み込みファイル数: ${ocrcount + 1}/${fileCount}<br>${fileName}`;
document.getElementById('fileinfo').innerHTML = fileinfo;
// ファイル送信実行部
call_execute_form(fileList[ocrcount]);
}
1ファイル目は、そのまま API リクエスト送信。
2ファイル目以降は、「次へ」ボタン押下時にリクエスト送信。
// API リクエスト処理
function call_execute_form(file) {
var url = "https://ocrxxx.jp/execute";
var content_type = "multipart/form-data";
var encoding = "UTF-8";
var method = "POST";
var data = [];
$("#api_result").text('OCR送信中 ...');
var fd = new FormData();
// リクエストに必要なフォーム値をセット
fd.append("request_id", 1);
fd.append("image_format", "JPEG");
fd.append("image_width", 100);
fd.append("image_height", 100);
// ...
fd.append("image_data", file);
$.ajax({
url: url,
contentType: false,
scriptCharset: encoding,
type: method,
processData: false,
data: fd,
dataType: 'JSON'
})
.done(function (data) {
if (data.results.length == 0) {
$("#api_result").text(data.message);
return;
}
var results = data.results[0];
var ocr_results = results.ocr_results;
if (ocr_results.length > 0) {
// 取得データ処理...
html += "<table>";
// ...
html += "</table>";
// 結果確認後の「次へ」ボタン
if (ocrcount !== (fileCount - 1)) {
html += '<input type="button" class="btn btn-primary" id="nextbtn" value="次へ" onClick="nextOcr();" />';
}
$("#api_result").html(html);
} else {
$("#api_result").text(JSON.stringify(data));
}
})
.fail(function (data) {
$("#api_result").text(JSON.stringify(data));
});
}
OCR で返ってきたレスポンス内容を確認して、次の画像ファイルを API リクエストを行うという処理になってます。
「input type="file"」に複数のファイルをアップロードすると、関数に渡ってきたオブジェクトの"files"プロパティに、ファイル1つずつが配列で格納されているので、1ファイルずつ順に処理しています。
元々、form リクエストを行う際に form タグに囲まれた input の内容をそれぞれ入力しておかなければいけない(もしくは Value 属性に値セットしておかなければいけない)と思い込んでいたのですが、formData オブジェクトを使ってリクエストを行えば、同等の効果が得られるようです。
今回は、わざわざボタン押下で次の画像リクエストに行くようにしていますが、for文で次々リクエストしてデータだけ配列に格納して最後に確認とかでもよかったかなと思います。