ファイルアップロードとは
ファイルアップロードは日常的な開発で広く利用されています。例えば、微博や微信朋友圈で写真を投稿する機能などがその一例です。
ブラウザの制約により、ブラウザは直接ファイルシステムを操作できません。ブラウザが提供する統一インターフェースを通じて、ユーザーが意図的にファイルアクセスアクションを開始し、ファイル内容を指定されたメモリに読み込み、最後にリクエストを送信してメモリ内のファイルデータをサーバーにアップロードします。サーバーはフロントエンドから送信されたデータ情報を解析し、ファイルに保存します。
ファイルアップロードでは、リクエストヘッダーをcontent-type:multipart/form-dataに設定する必要があります。
multipartはインターネット上の混合リソースを指し、リソースが複数の要素で構成されていることを意味します。form-dataはHTML FormsとPOSTメソッドを使用してファイルをアップロードできることを示します。
構造は以下の通りです:
POST /t2/upload.do HTTP/1.1
User-Agent: SOHUWapRebot
Accept-Language: zh-cn,zh;q=0.5
Accept-Charset: GBK,utf-8;q=0.7,*;q=0.7
Connection: keep-alive
Content-Length: 60408
Content-Type:multipart/form-data; boundary=ZnGpDtePMx0KrHh_G0X99Yef9r8JZsRJSXC
Host: w.sohu.com
--ZnGpDtePMx0KrHh_G0X99Yef9r8JZsRJSXC
Content-Disposition: form-data; name="city"
Santa colo
--ZnGpDtePMx0KrHh_G0X99Yef9r8JZsRJSXC
Content-Disposition: form-data;name="desc"
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
...
--ZnGpDtePMx0KrHh_G0X99Yef9r8JZsRJSXC
Content-Disposition: form-data;name="pic"; filename="photo.jpg"
Content-Type: application/octet-stream
Content-Transfer-Encoding: binary
... jpgのバイナリデータ ...
--ZnGpDtePMx0KrHh_G0X99Yef9r8JZsRJSXC--
boundaryは区切り文字を表し、複数のフォーム項目をアップロードする場合、boundaryを使用して分割します。各フォーム項目は———XXXで始まり、———XXXで終わります。
xxxは即時生成された文字列で、区切り文字全体がファイルやフォーム項目の内容に現れないことを保証するために使用されます。
各フォーム項目にはContent-Dispositionヘッダーが含まれている必要があり、その他のヘッダー情報はオプションです。例えばContent-Typeなどです。
Content-Dispositionにはtypeとnameという名前のparameterが含まれており、typeはform-dataで、nameパラメーターの値はフォームコントロール(フィールド)の名前です。ファイルの場合、filenameという追加のパラメーターがあり、その値がファイル名になります。
Content-Disposition: form-data; name="user"; filename="logo.png"
multipart/form-dataを使用する理由は、ファイルがバイナリ形式で存在するためです。その目的は、大規模なバイナリデータを効率的に転送することに特化しています。
ファイルアップロードの実装方法
ファイルアップロードは、主に二つのステップに分けることができます:
- ファイルのアップロード
- ファイルの解析
ファイルアップロード
従来のフロントエンドファイルアップロードフォームの構造は以下の通りです:
<form action="http://localhost:8080/api/upload" method="post" enctype="multipart/form-data">
<input type="file" name="file" id="file" value="" multiple="multiple" />
<input type="submit" value="送信"/>
</form>
actionはリクエストを送信するインターフェース、enctype="multipart/form-data"はアップロードファイルの形式を指定します。inputのname属性は必ずfileと等しくする必要があります。
ファイルの解析
ファイル解析
multipartyモジュールを使用してファイルを解析します:
const express = require('express');
const multer = require('multer');
const path = require('path');
const fs = require('fs');
const router = express.Router();
const upload = multer({ dest: 'uploads/' });
router.post('/upload', upload.single('filedata'), (req, res) => {
try {
const uploadedFile = req.file;
const fileData = fs.readFileSync(uploadedFile.path);
const targetPath = path.join(__dirname, '..', '/public/images', uploadedFile.originalname);
fs.writeFileSync(targetPath, fileData);
res.json({
code: 200,
message: 'ファイルアップロード成功',
url: '/images/' + uploadedFile.originalname
});
} catch (error) {
res.json({
code: 0,
message: 'ファイルアップロード失敗'
});
}
});
このルートミドルウェアでは、multerフォームオブジェクトを作成し、parseメソッドを呼び出してリクエストボディを解析します。コールバック関数には3つのパラメーターがあります:err(エラーオブジェクト)、fields(フォームフィールドオブジェクト)、file(ファイルオブジェクト)。file.pathを使用してファイルのキャッシュパスを取得し、fsモジュールのreadFileSyncとwriteFileSyncを使用してファイルを一時ディレクトリからターゲットディレクトリに移動します。
AJAXを使用したアップロードも可能です:
<script>
const fileInput = document.getElementById('ajaxFile');
const uploadButton = document.querySelector('button');
fileInput.addEventListener('change', function() {
console.log(fileInput.files);
});
uploadButton.addEventListener('click', function() {
const xhr = new XMLHttpRequest();
xhr.open('POST', 'http://localhost:8080/user/upload');
const formData = new FormData();
formData.append('filedata', fileInput.files[0]);
xhr.send(formData);
xhr.onload = function() {
const response = JSON.parse(xhr.responseText);
document.getElementById('uploadedImage').src = 'http://localhost:8080' + response.url;
};
});
</script>
インターフェースにリクエストを送信し、応答されたアドレスを取得して、取得した画像アドレスをレンダリングします。