目標
クライアントがアップロードした画像データをサーバーサイドに送信する
やり方
色々やり方はあるが、今回は'Content-Type': 'application/octet-stream'
を使用。
'Content-Type': 'application/octet-stream'
任意のバイナリデータ(="汎用のバイト列")を送信するためのMINEタイプ
これを使用すると、ブラウザ側のblobを直接body部に指定してバイナリデータを送信できる。
『blob』はあくまでブラウザ側のjavascriptでバイナリを扱う形式
サーバーサイド側で、『buffer』という別形式に変換されることに注意(後述)
⭐blobはバイナリとコンテンツタイプデータを持つが、bufferはコンテンツタイプデータを持たない。
そのため、image/jpegのようなデータもセットで送信しなければ復元が難しくなる。
⭐参考記事
React側コード例
const res = await fetch('/submitdata', {
method: 'POST',
headers: {
'Content-Type': 'application/octet-stream'
'X-type ':upload_image.type, // Blobのコンテンツタイプ
},
body: upload_image // Blob or File
});
upload_imageはblob(画像)です。
Fileでも送信できますが、今回はBlobでやります。
Express側コード例
app.post('/submitdata',(req, res) => {
// console.log('Request Headers:', req);
// console.log(req.headers['x-genre'])
// console.log(req.headers['x-description'])
console.log(body);
res.json({
body
});
})
実際に送信して、Express側のコンソールを確認します。
Bufferデータとして無事届いている。
あとはこのバイナリデータをDBやストレージに保存すればよい。
BufferデータをBlobに変換
Bufferデータはブラウザ側のjavascriptに適した形式ではないため、Blobに変換が必要
下記のコードで変換可能(jpegと仮定)
const uint8Array = new Uint8Array(buffer);
const blob = new Blob([uint8Array], { type: 'image/jpeg' });
検証
下記コードでbufferをblob変換してURL作成&srcに指定してブラウザ上で正常に表示されることを確認する
const [test,testUpdate] = useState();
const uint8Array = new Uint8Array(responsejson.body.data);
const blob = new Blob([uint8Array], { type: 'image/jpeg' }); // 必要に応じて 'image/png' など
testUpdate(URL.createObjectURL(blob));
// html部分割愛
<img src={test}></img>
応用
'Content-Type': 'application/octet-stream'
では、bodyにバイナリデータしか指定できない。
しかし、画像のメタデータやなんかしらの情報なども一緒に送信したくなる。
そんな時はヘッダーに追加することになる。がHTTPヘッダーは日本語を使用するとエラーになる。
そのため、文字データはURLエンコードした上でヘッダーに乗せる必要があり。
ヘッダー追加例
今回は
『画像のジャンル』
『画像の説明』
をヘッダーに指定する。
下記コードがヘッダー追加例。
⭐フロント側
//ジャンル,説明をエンコードしてヘッダーに乗せる 画像はbodyに乗せる
const genre = "海の画像"
const description = "きれいな海の画像です"
const res = await fetch('/submitdata', {
method: 'POST',
headers: {
'Content-Type': 'application/octet-stream',
'X-Genre': encodeURIComponent(genre),
'X-Description': encodeURIComponent(description),
},
body: upload_image_blob // Blob or File
});
⭐バックエンド側
app.post('/submitdata',(req, res) => {
const decodedGenre = decodeURIComponent(req.headers['x-genre']);
const decodedDescription = decodeURIComponent(req.headers['x-description']);
const body = req.body
console.log(decodedGenre);
console.log(decodedDescription);
console.log(body);
res.json({
body
});
})
ちなみにバックエンド側ではヘッダー指定する文字列は小文字にしないとエラーになります(理由は知りません)
'X-Genre' ⇒ ✖ 'x-genre' ⇒ 〇
encodeURIComponent() ⇒ 非ASCII文字列をバイト列に変換する。
こうすることで日本語などの非ASCII文字列をヘッダーに乗せて送信できる。
HTTPヘッダーはbodyと比較してサイズ制限が厳しい(8KB~16KBが一般的?)
ヘッダーサイズが大きいとエラー(413)になることに注意。
とはいえ、ヘッダーサイズ上限はWEBサーバーの設定で変えられる上、フロント側で制限すれば問題になることはあまりない印象。(ユーザー入力等を制限なしで受け付けると事故率は高まるが。)