はじめに
備忘録として残しています。
より良い実装方法があれば、指摘していただけると幸いです。
コード
page.tsx
"use client";
import { useState, useCallback } from "react";
import composeFiles from "@/utils/composeFiles";
(styled-componentで作成したものは省略)
export default function Home() {
const [image, setImage] = useState<File | null>(null);
const [video, setMovie] = useState<File | null>(null);
const [modalIsOpen, setModalIsOpen] = useState(false);
const [modalMessage, setModalMessage] = useState("");
// ダウンロードURLとして使用する
const [videoUrl, setVideoUrl] = useState<string | null>(null);
const handleChangeImage = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
(中略)
}, []);
const handleChangeVideo = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
(中略)
}, []);
const handleSubmit = async (e: React.MouseEvent<HTMLButtonElement>) => {
e.preventDefault();
if (image === null || video === null) {
openModal("画像または動画がアップロードされていません");
return;
}
const response = await composeFiles(image, video);
setVideoUrl(response);
};
return (
<main>
<Explanation />
<UploadForm
handleChangeImage={handleChangeImage}
handleChangeVideo={handleChangeVideo}
handleSubmit={handleSubmit}
/>
<PreviewImage file={image} />
<PreviewVideo file={video} />
{videoUrl && (
<div>
<a href={videoUrl} download="processed_video.mp4">
ダウンロード
</a>
<video controls src={videoUrl} />
</div>
)}
</main>
);
}
aタグをクリックすると、APIから返ってきた動画をダウンロードすることができます。
composeFile.tsx
// page.tsxから呼び出される
// /api/routes.tsにリクエストを送る
"use client";
import axios from "axios";
const composeFiles = async (image: File, video: File) => {
const formData = new FormData();
formData.append("image", image);
formData.append("video", video);
const response = await axios.post(`/api/compose`, formData, {
headers: {
"Content-Type": "multipart/form-data",
},
responseType: "blob",
});
const blob = response.data;
const url = URL.createObjectURL(blob);
return url;
};
export default composeFiles;
response.dataをblobとして受け取り、URL.createObjectURL(blob)でダウンロードするのに使用する一時的なURLを作成しています。
routes.ts
import { NextRequest, NextResponse } from "next/server";
import axios from "axios";
export async function POST(req: NextRequest) {
try {
const formData = await req.formData();
const response = await axios.post(`http://localhost:8080/compose`, formData, {
headers: { "Content-Type": "multipart/form-data" },
responseType: "stream",
});
return new NextResponse(response.data, {
status: 200,
headers: { "Content-Type": "video/mp4" },
});
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (error: any) {
console.error("Error uploading files:", error);
return NextResponse.json({ message: "Error", error }, { status: 500 });
}
}
特に変わったことはしていないと思います。
リクエストデータからformDataを受け取り、APIに渡しています。
リクエスト先のAPIでは受け取った画像と動画を合成しています。
こちらの記事に載せているものをFastAPIに移植しました。
まとめ
APIから返ってきた動画をblob
として受け取って、URL.createObjectURL
で一時的なURLを作成しています。
そのURLをaタグのhrefに指定したうえで、download="{任意のファイル名}.mp4"を指定することでダウンロードリンクを作成することができます。
<a href={videoUrl} download="video_filename.mp4">ダウンロードリンク<a/>
受け取ったURLから動画を表示させたいのですが、下記のコードでは表示できないです。
良い方法をご存知であれば教えていただけると幸いです。
<video controls src={videoUrl} />