refactor: tus 업로드 커스텀 훅 생성
This commit is contained in:
@@ -1,61 +0,0 @@
|
|||||||
import { Upload, type HttpResponse } from "tus-js-client";
|
|
||||||
import { getCookie, setCookie } from "@/shared/lib";
|
|
||||||
import { AUTH_USER_ID, CLAIM_NAME, ENDPOINT } from "@/shared/config";
|
|
||||||
|
|
||||||
type UploadOptions = {
|
|
||||||
handleUploadStart?: (upload: Upload) => void;
|
|
||||||
handleProgress?: (percentage: string) => void;
|
|
||||||
handleSuccess?: (upload: Upload) => void;
|
|
||||||
handleResponse?: (res: HttpResponse) => void;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const handleUpload = (files: FileList, options?: UploadOptions) => {
|
|
||||||
let metadata = {};
|
|
||||||
|
|
||||||
const file = files.item(0);
|
|
||||||
if (!file) return;
|
|
||||||
metadata = {
|
|
||||||
filename: file.name,
|
|
||||||
filetype: file.type,
|
|
||||||
};
|
|
||||||
|
|
||||||
const atflCd = getCookie("atfl-cd-key");
|
|
||||||
if (atflCd) {
|
|
||||||
metadata = {
|
|
||||||
...metadata,
|
|
||||||
atflCd,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const upload = new Upload(file, {
|
|
||||||
endpoint: `${ENDPOINT.ATTACH_SERVICE.TUS_UPLOAD}`,
|
|
||||||
chunkSize: 10 * 1024 * 1024,
|
|
||||||
headers: {
|
|
||||||
[CLAIM_NAME]: getCookie(CLAIM_NAME) ?? "",
|
|
||||||
[AUTH_USER_ID]: getCookie(AUTH_USER_ID) ?? "",
|
|
||||||
},
|
|
||||||
metadata,
|
|
||||||
onError(error) {
|
|
||||||
console.error(`Failed because: ${error}`);
|
|
||||||
},
|
|
||||||
onProgress(bytesUploaded, bytesTotal) {
|
|
||||||
const percentage = ((bytesUploaded / bytesTotal) * 100).toFixed(2);
|
|
||||||
options?.handleProgress?.(percentage);
|
|
||||||
},
|
|
||||||
onAfterResponse(req, res) {
|
|
||||||
const fileInfo = res.getHeader("X-File-Info");
|
|
||||||
if (fileInfo === undefined || fileInfo === null) return;
|
|
||||||
|
|
||||||
setCookie("atfl-cd-key", JSON.parse(fileInfo)?.atflCd ?? "");
|
|
||||||
options?.handleResponse?.(res);
|
|
||||||
},
|
|
||||||
onSuccess() {
|
|
||||||
options?.handleSuccess?.(upload);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
upload.start();
|
|
||||||
options?.handleUploadStart?.(upload);
|
|
||||||
|
|
||||||
console.log("upload start >>>>>", upload);
|
|
||||||
};
|
|
||||||
@@ -4,7 +4,7 @@ import Image from "next/image";
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { useQuery } from "@tanstack/react-query";
|
import { useQuery } from "@tanstack/react-query";
|
||||||
import type { UseFormReturn } from "react-hook-form";
|
import type { UseFormReturn } from "react-hook-form";
|
||||||
import type { HttpResponse, Upload } from "tus-js-client";
|
import type { HttpResponse } from "tus-js-client";
|
||||||
import {
|
import {
|
||||||
FileUploader,
|
FileUploader,
|
||||||
Input,
|
Input,
|
||||||
@@ -22,10 +22,9 @@ import { FormWrapper } from "@/widgets/form-wrapper";
|
|||||||
import { CodeSelect } from "@/entities/code";
|
import { CodeSelect } from "@/entities/code";
|
||||||
import { AttachmentApi } from "@/entities/attachment";
|
import { AttachmentApi } from "@/entities/attachment";
|
||||||
import { ENDPOINT } from "@/shared/config";
|
import { ENDPOINT } from "@/shared/config";
|
||||||
import { setCookie } from "@/shared/lib/cookie";
|
import { useTus } from "@/shared/lib/use-tus";
|
||||||
import { FormControl, FormField, FormItem } from "@/shared/ui/form";
|
import { FormControl, FormField, FormItem } from "@/shared/ui/form";
|
||||||
import type { ImageFormData } from "../model/image-interface";
|
import type { ImageFormData } from "../model/image-interface";
|
||||||
import { handleUpload } from "../lib/utils";
|
|
||||||
|
|
||||||
type ImageFormProps = {
|
type ImageFormProps = {
|
||||||
form: UseFormReturn<ImageFormData>;
|
form: UseFormReturn<ImageFormData>;
|
||||||
@@ -38,9 +37,13 @@ function ImageForm({ form, imgSn, ...props }: ImageFormProps) {
|
|||||||
const atflYn = form.watch("atflYn");
|
const atflYn = form.watch("atflYn");
|
||||||
const atflCd = form.watch("atflCd");
|
const atflCd = form.watch("atflCd");
|
||||||
|
|
||||||
|
const {
|
||||||
|
file: uploadFile,
|
||||||
|
response: uploadResponse,
|
||||||
|
progress,
|
||||||
|
setFile,
|
||||||
|
} = useTus();
|
||||||
const [imageUrl, setImageUrl] = useState("");
|
const [imageUrl, setImageUrl] = useState("");
|
||||||
const [upload, setUpload] = useState<Upload | null>(null);
|
|
||||||
const [progress, setProgress] = useState(0);
|
|
||||||
|
|
||||||
const { data: attachmentData } = useQuery({
|
const { data: attachmentData } = useQuery({
|
||||||
queryKey: [ENDPOINT.ATTACH_SERVICE.ATTACHMENTS, atflCd],
|
queryKey: [ENDPOINT.ATTACH_SERVICE.ATTACHMENTS, atflCd],
|
||||||
@@ -49,10 +52,6 @@ function ImageForm({ form, imgSn, ...props }: ImageFormProps) {
|
|||||||
enabled: !!atflCd,
|
enabled: !!atflCd,
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleProgress = (percentage: string) => {
|
|
||||||
setProgress(Number(percentage));
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleUploadResponse = async (res: HttpResponse) => {
|
const handleUploadResponse = async (res: HttpResponse) => {
|
||||||
const fileInfo = res.getHeader("X-File-Info");
|
const fileInfo = res.getHeader("X-File-Info");
|
||||||
if (fileInfo === undefined || fileInfo === null) return;
|
if (fileInfo === undefined || fileInfo === null) return;
|
||||||
@@ -64,6 +63,11 @@ function ImageForm({ form, imgSn, ...props }: ImageFormProps) {
|
|||||||
await AttachmentApi.deleteAttachmentByAtflCd(attachmentData.atflCd);
|
await AttachmentApi.deleteAttachmentByAtflCd(attachmentData.atflCd);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!uploadResponse) return;
|
||||||
|
handleUploadResponse(uploadResponse);
|
||||||
|
}, [uploadResponse]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
(async () => {
|
(async () => {
|
||||||
setImageUrl(
|
setImageUrl(
|
||||||
@@ -197,13 +201,7 @@ function ImageForm({ form, imgSn, ...props }: ImageFormProps) {
|
|||||||
onUpload={(e) => {
|
onUpload={(e) => {
|
||||||
const files = e.target.files;
|
const files = e.target.files;
|
||||||
if (!files) return;
|
if (!files) return;
|
||||||
setCookie("atfl-cd-key", "");
|
setFile(files[0] ?? null);
|
||||||
handleUpload(files, {
|
|
||||||
handleUploadStart: setUpload,
|
|
||||||
handleProgress,
|
|
||||||
handleSuccess: setUpload,
|
|
||||||
handleResponse: handleUploadResponse,
|
|
||||||
});
|
|
||||||
}}
|
}}
|
||||||
customPreview={
|
customPreview={
|
||||||
<>
|
<>
|
||||||
@@ -220,7 +218,7 @@ function ImageForm({ form, imgSn, ...props }: ImageFormProps) {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<p className="absolute bottom-3 left-0 z-10 p-2 text-base leading-none">
|
<p className="absolute bottom-3 left-0 z-10 p-2 text-base leading-none">
|
||||||
{(upload?.file as File)?.name}
|
{uploadFile?.name}
|
||||||
</p>
|
</p>
|
||||||
<Progress
|
<Progress
|
||||||
value={progress}
|
value={progress}
|
||||||
|
|||||||
@@ -0,0 +1,64 @@
|
|||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import { type HttpResponse, Upload } from "tus-js-client";
|
||||||
|
import { AUTH_USER_ID, CLAIM_NAME, ENDPOINT } from "../config";
|
||||||
|
import { useAttachmentStore } from "./use-attachment-store";
|
||||||
|
import { getCookie } from "./cookie";
|
||||||
|
|
||||||
|
export const useTus = () => {
|
||||||
|
const { attachmentFileCode, setAttachmentFileCode } = useAttachmentStore(
|
||||||
|
(state) => state,
|
||||||
|
);
|
||||||
|
|
||||||
|
const [file, setFile] = useState<File | null>(null);
|
||||||
|
const [upload, setUpload] = useState<Upload | null>(null);
|
||||||
|
const [response, setResponse] = useState<HttpResponse | null>(null);
|
||||||
|
const [progress, setProgress] = useState<number>(0);
|
||||||
|
|
||||||
|
const metadata = {
|
||||||
|
filename: file?.name ?? "",
|
||||||
|
filetype: file?.type ?? "",
|
||||||
|
atflCd: attachmentFileCode,
|
||||||
|
};
|
||||||
|
|
||||||
|
const createUpload = () => {
|
||||||
|
return new Upload(file!, {
|
||||||
|
endpoint: `${ENDPOINT.ATTACH_SERVICE.TUS_UPLOAD}`,
|
||||||
|
chunkSize: 10 * 1024 * 1024,
|
||||||
|
headers: {
|
||||||
|
[CLAIM_NAME]: getCookie(CLAIM_NAME) ?? "",
|
||||||
|
[AUTH_USER_ID]: getCookie(AUTH_USER_ID) ?? "",
|
||||||
|
},
|
||||||
|
metadata,
|
||||||
|
onError(error) {
|
||||||
|
throw new Error(`Failed because: ${error}`);
|
||||||
|
},
|
||||||
|
onProgress(bytesUploaded, bytesTotal) {
|
||||||
|
const percentage = ((bytesUploaded / bytesTotal) * 100).toFixed(2);
|
||||||
|
setProgress(Number(percentage));
|
||||||
|
},
|
||||||
|
onAfterResponse(req, res) {
|
||||||
|
const fileInfo = res.getHeader("X-File-Info");
|
||||||
|
if (fileInfo === undefined || fileInfo === null) return;
|
||||||
|
|
||||||
|
setAttachmentFileCode(JSON.parse(fileInfo)?.atflCd ?? "");
|
||||||
|
setResponse(res);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!file) return;
|
||||||
|
|
||||||
|
const upload = createUpload();
|
||||||
|
upload.start();
|
||||||
|
setUpload(upload);
|
||||||
|
}, [file]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
file,
|
||||||
|
upload,
|
||||||
|
response,
|
||||||
|
progress,
|
||||||
|
setFile,
|
||||||
|
};
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user