Node.jsでのファイルアップロードの実装方法

ファイルアップロードとは

ファイルアップロードは日常的な開発で広く利用されています。例えば、微博や微信朋友圈で写真を投稿する機能などがその一例です。

ブラウザの制約により、ブラウザは直接ファイルシステムを操作できません。ブラウザが提供する統一インターフェースを通じて、ユーザーが意図的にファイルアクセスアクションを開始し、ファイル内容を指定されたメモリに読み込み、最後にリクエストを送信してメモリ内のファイルデータをサーバーにアップロードします。サーバーはフロントエンドから送信されたデータ情報を解析し、ファイルに保存します。

ファイルアップロードでは、リクエストヘッダーを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にはtypenameという名前のparameterが含まれており、typeform-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"はアップロードファイルの形式を指定します。inputname属性は必ず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>

インターフェースにリクエストを送信し、応答されたアドレスを取得して、取得した画像アドレスをレンダリングします。

タグ: Node.js multipart/form-data file-upload express FormData

6月12日 19:23 投稿