🗂

Postman : 画像アップローダーのテスト、バイナリデータのアップロードをテストする

に公開

Postmanコレクションでは、REST APIのテストの際に、POSTするデータは単純な文字列やJSONだけではなく、バイナリーデータや複数のフィールド/パートをPOSTできます。

この記事ではform-dataバイナリの2種類を試していきます。

form-data

form-data は、HTTP リクエストで ファイルを含むフォームデータや複雑なデータ を送信するためのエンコード方式です。正式には multipart/form-data という名前で、Content-Type: multipart/form-data として送られます。

バイナリ

ファイルそのものをリクエストボディに“生”で送信する方法です。Content-Type は通常 application/octet-stream になりますが、Postmanでは自動的にアップロードを行うファイル種別を判別してContent-Typeをセットします。例えばJPEGファイルの場合image/jpegとなります。

さっそくやってみる

この2つの機能を試すファイルアップローダを作成しPostmanでテストを行います。
まず以下2つのファイルを作成します。

package.json
{
  "name": "jpeg-uploader",
  "version": "1.0.0",
  "description": "Accept and save JPEG via multipart/form-data or raw image/jpeg POST",
  "type": "module",
  "main": "index.js",
  "scripts": {
    "start": "node index.js",
    "dev": "node --watch index.js"
  },
  "dependencies": {
    "express": "^5.1.0",
    "multer": "^1.4.5-lts.1",
    "uuid": "^9.0.1"
  }
}
index.js
import express from "express";
import multer from "multer";
import { promises as fsp } from "fs";
import path from "path";
import { fileURLToPath } from "url";
import { v4 as uuidv4 } from "uuid";

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

const app = express();
const PORT = process.env.PORT || 3000;
const UPLOAD_DIR = path.join(__dirname, "uploads");

// ===== 共通: アップロード先の用意 =====
async function ensureUploadDir() {
  try {
    await fsp.mkdir(UPLOAD_DIR, { recursive: true });
  } catch (e) {
    console.error("Failed to create upload dir:", e);
    process.exit(1);
  }
}
await ensureUploadDir();

// ===== 1) multipart/form-data 用エンドポイント =====
// メモリに載せず、直接ディスクへ(JPEGのみ保存)
const storage = multer.diskStorage({
  destination: (req, file, cb) => cb(null, UPLOAD_DIR),
  filename: (req, file, cb) => {
    // 元ファイル名を信用しないで、UUID + .jpg で保存
    const id = uuidv4();
    cb(null, `${id}.jpg`);
  }
});

const fileFilter = (req, file, cb) => {
  if (file.mimetype === "image/jpeg") {
    cb(null, true);
  } else {
    cb(new Error("Only JPEG is allowed"));
  }
};

const upload = multer({
  storage,
  fileFilter,
  limits: { fileSize: 10 * 1024 * 1024 } // 10MB
});

// フォーム側のフィールド名は "file" を想定
app.post("/upload-multipart", upload.single("file"), (req, res) => {
  // multer 成功時は req.file に情報
  const savedPath = path.relative(__dirname, req.file.path);
  return res.status(201).json({
    ok: true,
    method: "multipart",
    file: {
      filename: path.basename(savedPath),
      path: savedPath,
      size: req.file.size,
      mimetype: req.file.mimetype
    }
  });
});

// エラーハンドラ(multerのバリデーションなど)
app.use((err, req, res, next) => {
  if (err instanceof multer.MulterError || err.message === "Only JPEG is allowed") {
    return res.status(400).json({ ok: false, error: err.message });
  }
  return next(err);
});

// ===== 2) 生バイナリ(image/jpeg)用エンドポイント =====
// 例: fetch/curl でボディにJPEGを直接送る
app.post(
  "/upload-binary",
  express.raw({ type: "image/jpeg", limit: "10mb" }),
  async (req, res) => {
    try {
      if (!req.is("image/jpeg")) {
        return res.status(415).json({ ok: false, error: "Content-Type must be image/jpeg" });
      }
      if (!req.body || req.body.length === 0) {
        return res.status(400).json({ ok: false, error: "Empty body" });
      }

      const id = uuidv4();
      const filename = `${id}.jpg`;
      const fullpath = path.join(UPLOAD_DIR, filename);

      await fsp.writeFile(fullpath, req.body);

      return res.status(201).json({
        ok: true,
        method: "binary",
        file: {
          filename,
          path: path.relative(__dirname, fullpath),
          size: req.body.length,
          mimetype: "image/jpeg"
        }
      });
    } catch (e) {
      console.error(e);
      return res.status(500).json({ ok: false, error: "Internal error" });
    }
  }
);

// ヘルスチェック
app.get("/healthz", (_req, res) => res.json({ ok: true }));

app.listen(PORT, () => {
  console.log(`JPEG uploader listening on http://localhost:${PORT}`);
});

依存関係のあるパッケージをインストールした後起動します。

npm install express multer uuid
npm start

以下の様に表示されればポート3000番でテスト用アップローダが起動しています。

JPEG uploader listening on http://localhost:3000

/upload-multipart と /upload-binary

このテスト用アップローダは2つのエンドポイントを提供します。
/upload-multipartはPostmanのform-data用です。/upload-binaryバイナリ用です。それぞれアップロードされたJPEGファイルが生成されたuuidのファイル名でテスト用アップローダの場所(index.jsが存在している場所)に保存されます。

/upload-multipart のテスト

ボディタブからform-dataを選択して、適当なJPEGを指定します。

キーにはfileを指定します。

APIの呼び出し先をhttp://localhost:3000/upload-multipartに指定して送信ボタンをクリックします。JSONで以下の様なレスポンスが戻ればアップロード成功です。

{
    "ok": true,
    "method": "multipart",
    "file": {
        "filename": "10200c37-99c6-4255-806c-1bfcc1c3814d.jpg",
        "path": "uploads\\10200c37-99c6-4255-806c-1bfcc1c3814d.jpg",
        "size": 68099,
        "mimetype": "image/jpeg"
    }
}

JPEGファイルが一つ生成されていれば完了です。

Postmanからcurlコマンドを出力すると以下の様になっています。

curl --location 'http://localhost:3000/upload-multipart' \
--form 'file=@"/C:/Users/h-kameda/Desktop/449652678_7709529589129107_2176919591431881016_n.jpg"'

/upload-binary のテスト

次にボディタブからバイナリを指定します。

API呼び出し先をhttp://localhost:3000/upload-binaryとして送信ボタンをクリックします。
同様に以下の様なレスポンスが戻れば成功です。

{
    "ok": true,
    "method": "binary",
    "file": {
        "filename": "a4bab7de-522d-4a4e-af0d-8750de5dd92f.jpg",
        "path": "uploads\\a4bab7de-522d-4a4e-af0d-8750de5dd92f.jpg",
        "size": 68099,
        "mimetype": "image/jpeg"
    }
}

もう一つファイルができています。

Postmanが自動生成しているcurlコマンドは先ほどと異なりContent-Type:が自動で設定されています。

curl --location 'http://localhost:3000/upload-binary' \
--header 'Content-Type: image/jpeg' \
--data-binary '@/C:/Users/h-kameda/Desktop/449652678_7709529589129107_2176919591431881016_n.jpg'

Content-Type の変更

Content-Typeを変更したい場合以下の様に、自動で作成されたもののチェックを外して、あたらに作成して任意の値を指定します。これにより、自動作成されたものが上書きされます。

Discussion