commit be557ea7210cc68adb3b013ab5299203ce78a172
parent c97af9d73b93b90d150d48b6b2b03fdfedccc5f7
Author: 諒太 林 <ryouturn@gmail.com>
Date: Tue, 16 Jul 2024 21:33:23 +0900
add uploader
Diffstat:
5 files changed, 36 insertions(+), 189 deletions(-)
diff --git a/README.md b/README.md
@@ -40,6 +40,9 @@ flowchart
upload formの参考
https://tech.andpad.co.jp/entry/2023/12/10/100000
+fileUploaderの参考
+https://zenn.dev/moozaru/articles/b6d46e5787f052
+
## 開発マニュアル(簡易版)
リンクしてあるProjectのTodoを見て、できそうなことがあったら、自分をassignしてください。
作業は自分のところにforkしてもらって、プルリクを投げてください。
diff --git a/src/app/fileUploader.ts b/src/app/fileUploader.ts
@@ -0,0 +1,19 @@
+"use server";
+import { promises as fs } from "node:fs";
+import { resolve } from "node:path";
+import { revalidatePath } from "next/cache";
+
+export async function uploadFile(formData: FormData) {
+ const file = formData.get("file") as File;
+ if (file && file.size > 0) {
+ const data = await file.arrayBuffer();
+ const buffer = Buffer.from(data);
+ const filePath = resolve(
+ process.cwd(),
+ "./public/uploads",
+ `${crypto.randomUUID()}.${file.name.split(".").pop()}`,
+ );
+ await fs.writeFile(filePath, buffer);
+ }
+ revalidatePath("/file");
+}+
\ No newline at end of file
diff --git a/src/app/fileuploadform.tsx b/src/app/fileuploadform.tsx
@@ -1,173 +1,12 @@
-import type { NextPage } from "next";
-import React, { useState, useCallback } from "react";
-import { useDropzone } from "react-dropzone";
-import type { FileRejection } from "react-dropzone";
-
-const FileUploader: NextPage = () => {
- const [currentShowFiles, setCurrentShowFiles] = useState<
- { file: File; isUploaded: boolean }[]
- >([]);
-
- const onUploadFile = async (file: File) => {
- try {
- setCurrentShowFiles((prevFiles) => [
- ...prevFiles,
- { file, isUploaded: false },
- ]);
-
- const uploadTime = Math.random() * 9000 + 1000; // 1秒から10秒
- await new Promise((resolve) => setTimeout(resolve, uploadTime));
-
- setCurrentShowFiles((prevFiles) =>
- prevFiles.map((f) =>
- f.file.name === file.name ? { ...f, isUploaded: true } : f,
- ),
- );
- } catch (error) {
- // ↓ここでエラーに関するユーザーへの通知や処理を行う
- alert(`アップロード中にエラーが発生しました: ${error}`);
- }
- };
-
- const onDrop = useCallback(
- async (acceptedFiles: File[]) => {
- // ドロップしたファイルの中で、現在表示されているファイルと重複しているもの( filename と size が同じファイル)を除外する。
- const filteringFiles = acceptedFiles.filter(
- (file) =>
- !currentShowFiles?.find(
- (showFile) =>
- file.name === showFile.file.name &&
- file.size === showFile.file.size,
- ),
- );
-
- // ドロップしたファイルと現在表示されているファイルの合計が 10 を超える場合、追加を許可しない。
- if (filteringFiles.length + currentShowFiles.length > 10) {
- alert("最大10ファイルまでアップロードできます。");
- return;
- }
-
- // アップロード可能なファイルが存在する場合、アップロード中のスイッチを true にし、アップロードを開始する
- if (filteringFiles.length) {
- try {
- await Promise.all(filteringFiles.map((file) => onUploadFile(file)));
- // ↓すべてのファイルのアップロードが成功した後の処理を書く
- } catch (error) {
- // ↓ここでエラーに関するユーザーへの通知や処理を行う
- alert(`アップロード中にエラーが発生しました: ${error}`);
- }
- }
- },
- [currentShowFiles],
- );
-
- const onDropRejected = useCallback((rejectedFiles: FileRejection[]) => {
- rejectedFiles.forEach(({ file, errors }) => {
- errors.forEach(({ code }) => {
- let message = "エラーが発生しました。";
- switch (code) {
- case "file-too-large":
- message = `${file.name} のファイルサイズが大きすぎます。50MB以下のファイルをアップロードしてください。`;
- break;
- case "file-invalid-type":
- message = `${file.name} のファイル形式が許可されていません。許可されているファイル形式は jpg, png, pdf, doc, docx, xls, xlsx, ppt, pptx です。`;
- break;
- default:
- break;
- }
- alert(message);
- });
- });
- }, []);
-
- const { getRootProps, getInputProps, isDragAccept, isDragReject } =
- useDropzone({
- onDrop,
- onDropRejected,
- accept: {
- "image/jpeg": [],
- "image/png": [],
- "application/pdf": [],
- },
- maxSize: 50 * 1024 * 1024, // 50MB
- });
-
- // ドラッグ中のスタイルを設定
-
- const removeFile = (index: number) => {
- const filteringFiles = currentShowFiles.filter(
- (_, i) => i !== index,
- );
- setCurrentShowFiles(filteringFiles);
- };
-
- return (
- <div>
- <div>
- <div
- {...getRootProps()}
- >
- <input {...getInputProps()} />
- <p>
- {isDragAccept
- ? "ファイルをアップロードします。"
- : isDragReject
- ? "エラー"
- : "ファイルを登録してください。"}
- </p>
- <p>
- {isDragReject
- ? "このファイル形式のアップロードは許可されていません。"
- : "ファイルを選択するか、ドラッグアンドドロップしてください。"}
- </p>
- <button disabled={isDragReject}>ファイルを選択</button>
- </div>
- <p>
- 複数のファイルを選択できます。pdf, png, jpg, jpeg
- ファイルを選択できます。
- </p>
- <p>※1ファイルの最大サイズは50MBです</p>
- </div>
- {currentShowFiles && (
- <aside>
- <ul>
- {currentShowFiles.map((item, index) => (
- <li key={index}>
- {item.isUploaded ? (
- <div>
- <div>
- <span>
- </span>
- </div>
- <div>
- <p>{item.file.name}</p>
- </div>
- <button
- type="button"
- onClick={() => {
- removeFile(index);
- }}
- >
- <span>
- </span>
- </button>
- </div>
- ) : (
- <div>
- <div>
- <p>
- {item.file.name}をアップロードしています…
- </p>
- </div>
- </div>
- )}
- </li>
- ))}
- </ul>
- </aside>
- )}
- </div>
- );
+'use client';
+import { Button, Input } from "@mui/material";
+import { uploadFile } from "./fileUploader";
+
+export default function UploadForm() {
+ return(
+ <form action={uploadFile}>
+ <Input type="file" name="file" sx={{marginRight:'10px'}}/>
+ <Button type="submit" variant="contained" size="small">アップロード</Button>
+ </form>
+ )
};
-
-export default FileUploader;-
\ No newline at end of file
diff --git a/src/app/page.tsx b/src/app/page.tsx
@@ -17,7 +17,7 @@ import MuiAppBar, { AppBarProps as MuiAppBarProps } from '@mui/material/AppBar';
import IconButton from '@mui/material/IconButton';
import ChevronLeftIcon from '@mui/icons-material/ChevronLeft';
import ChevronRightIcon from '@mui/icons-material/ChevronRight';
-import FileUploader from './fileUploadForm';
+import UploadForm from './fileUploadForm';
import HomeIcon from '@mui/icons-material/Home';
import ImageSearchIcon from '@mui/icons-material/ImageSearch';
import HelpIcon from '@mui/icons-material/Help';
@@ -160,7 +160,7 @@ export default function Home() {
<p>Files posted here will be deleted after a set period of time.</p>
</div>
<Box>
- <FileUploader/>
+ <UploadForm/>
</Box>
<Box>
<h4>URLs</h4>
diff --git a/src/app/server.ts b/src/app/server.ts
@@ -1,13 +0,0 @@
-'use server'
-import fs from "node:fs/promises";
-import { revalidatePath } from "next/cache";
-
-export async function uploadFile(formData: FormData) {
- const file = formData.get("file") as File;
- const arrayBuffer = await file.arrayBuffer();
- const buffer = new Uint8Array(arrayBuffer);
-
- await fs.writeFile(`./public/uploads/${file.name}`, buffer);
-
- revalidatePath("/");
-}-
\ No newline at end of file