0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

はじめに

備忘録として残しています。
より良い実装方法があれば、指摘していただけると幸いです。

コード

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} />
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?