はじめに
APIルートでCSVファイルを送る必要があり、実装していたところ、APIルートでCSVが文字列で受け取ったり実装に苦労したため、備忘録として残します。
環境
csvファイルをパースするために、multiparty
を使用します。
"next": "13.2.4",
"multiparty": "^4.2.3",
コード
HTTPリクエストにaxios
を使用していますが、何でも良いです。
filePost.ts
import axios from 'axios'
export const filePost = async (params: FormData) => {
await axios.post('/api/post', params, {
headers: {
ContentType: 'multipart/form-data',
},
});
};
src/pages/api/post.ts
import axios from 'axios';
import { createFormDataFromMultipartyParsedFiles } from '@/utils/createFormDataFromMultipartyParsedFiles';
import { parseNextApiRequest } from '@/utils/parseNextApiRequest';
import type { NextApiRequest, NextApiResponse } from 'next';
export default async function handler(
req: NextApiRequest,
res: NextApiResponse,
) {
if (req.method === 'POST') {
(async () => {
try {
// リクエストで受けとったCSVファイルをパースする
const formParsedData = await parseNextApiRequest(req);
if ('error' in formParsedData) {
return;
}
// パースしたデータから、formDataを生成
const formData = createFormDataFromMultipartyParsedFiles(
formParsedData.files,
);
// ファイルを送信
await axios.post(<URL>, formData);
return res.status(200).json(response.data);
} catch (error: any) {
return res.status(500).json(error.response.data);
}
})();
}
}
// APIルートで受け取ったリクエストをパースしないようにする
export const config = {
api: {
bodyParser: false,
},
};
src/utils/createFormDataFromMultipartyParsedFiles.ts
import fs from 'fs';
import type multiparty from 'multiparty';
type MultipartyParsedValues = {
fields: Record<string, string[] | undefined>;
files: Record<string, multiparty.File[] | undefined>;
};
export const createFormDataFromMultipartyParsedFiles = (
files: MultipartyParsedValues['files'],
) => {
const formData = new FormData();
Object.values(files).forEach((files) => {
if (!files) {
return;
}
const file = files[0];
// ファイルを読み込んで、ファイルオブジェクトを生成
const fileContent = fs.readFileSync(file.path);
const blob = new Blob([fileContent], {
type: file.headers['content-type'],
});
const processedFile = new File([blob], file.originalFilename);
formData.append(file.fieldName, processedFile);
});
return formData;
};
src/utils/parseNextApiRequest.ts
import multiparty from 'multiparty';
import type { NextApiRequest } from 'next';
type MultipartyParsedError = {
error: Error | null;
};
type MultipartyParsedValues = {
fields: Record<string, string[] | undefined>;
files: Record<string, multiparty.File[] | undefined>;
};
export const parseNextApiRequest = async (req: NextApiRequest) => {
const form = new multiparty.Form();
const formParsedData = await new Promise<
MultipartyParsedError | MultipartyParsedValues
>((resolve, reject) => {
form.parse(req, (error, fields, files) => {
if (error) {
reject({ error });
}
resolve({ fields, files });
});
});
return formParsedData;
};
まとめ
APIルートで受け取ったリクエストをformData
に戻すのに苦労しました。外部モジュールの必要や処理が増えるため、中間APIが必要でなければ直接リクエストするのが良いかなと思います。
参考記事
最後に
GoQSystemでは一緒に働いてくれる仲間を募集中です!
ご興味がある方は以下リンクよりご確認ください。