ImageUploader

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

commit 1d75dddd4cff1a529bc67be5e8c7c0fd0b6868ff
parent 1a98b23bc5891fcc1c14ef160b8ab06365141010
Author: 諒太 林 <ryouturn@gmail.com>
Date:   Wed, 10 Jul 2024 18:10:43 +0900

add some code but it still not work

Diffstat:
MREADME.md | 7+++++--
Mimage-uploader/package.json | 3++-
Aimage-uploader/src/app/fileuploadform.tsx | 183+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mimage-uploader/src/app/page.tsx | 6+++---
4 files changed, 193 insertions(+), 6 deletions(-)

diff --git a/README.md b/README.md @@ -4,4 +4,7 @@ This app is creating for one-time image uploader in Pim. ## Design -This app use nextjs. Also it mean that it using nodejs and react.- \ No newline at end of file +This app use nextjs. Also it mean that it using nodejs and react. + +### 参考文献 +https://tech.andpad.co.jp/entry/2023/12/10/100000+ \ No newline at end of file diff --git a/image-uploader/package.json b/image-uploader/package.json @@ -21,5 +21,6 @@ "eslint": "^9.6.0", "eslint-config-next": "14.2.4", "typescript": "^5.5.3" - } + }, + "packageManager": "pnpm@9.5.0+sha512.140036830124618d624a2187b50d04289d5a087f326c9edfc0ccd733d76c4f52c3a313d4fc148794a2a9d81553016004e6742e8cf850670268a7387fc220c903" } diff --git a/image-uploader/src/app/fileuploadform.tsx b/image-uploader/src/app/fileuploadform.tsx @@ -0,0 +1,182 @@ +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 setDropZoneStyle = () => { + if (isDragAccept) { + return styles.is_drag_accept; + } else if (isDragReject) { + return styles.is_drag_reject; + } else { + return ""; + } + }; + + 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> + ); +}; + +export default FileUploader;+ \ No newline at end of file diff --git a/image-uploader/src/app/page.tsx b/image-uploader/src/app/page.tsx @@ -1,4 +1,5 @@ 'use client' +import FileUploader from "./fileuploadform"; import styles from "./page.module.css"; import { useDropzone } from "react-dropzone"; @@ -17,9 +18,8 @@ export default function Home() { <h3>Attention</h3> <p>Files posted here will be deleted after a set period of time.</p> </div> - <div {...getRootProps()} style={{ border: "1px solid #ff7f50", width: 200, height: 150 }}> - <input {...getInputProps()} /> - <button>upload</button> + <div> + <FileUploader/> </div> <div> <h4>URL</h4>