찾다
웹 프론트엔드JS 튜토리얼Documenso와 aws-smage-upload 예제의 Spload 기능 비교

이 글에서는 Documenso와 AWS S3 이미지 업로드 예시를 통해 AWS S3에 파일을 업로드하는 단계를 비교해 보겠습니다.

Vercel에서 제공하는 간단한 예제부터 시작하겠습니다.

Comparison of Spload feature between Documenso and aws-smage-upload example

예제/aws-s3-이미지 업로드

Vercel은 AWS S3에 파일을 업로드하는 좋은 작업 예제를 제공합니다.

이 예의 README는 기존 S3 버킷을 사용하거나 새 버킷을 생성하는 두 가지 옵션을 제공합니다. 이를 이해하는 것이 도움이 됩니다

업로드 기능을 올바르게 구성했습니다.

소스코드를 살펴볼 차례입니다. type=file인 입력 요소를 찾고 있습니다. app/page.tsx에서 아래 코드를 찾을 수 있습니다:

return (
    <main>
      <h1 id="Upload-a-File-to-S">Upload a File to S3</h1>
      <form onsubmit="{handleSubmit}">
        <input>



<h2>
  
  
  <strong>onChange</strong>
</h2>

<p>onChange updates state using setFile, but it does not do the uploading. upload happens when you submit this form.<br>
</p>

<pre class="brush:php;toolbar:false">onChange={(e) => {
  const files = e.target.files
  if (files) {
    setFile(files[0])
  }
}}

제출 처리

handleSubmit 함수에서는 많은 일이 일어나고 있습니다. 이 handlerSubmit 함수의 작업 목록을 분석해야 합니다. 단계를 설명하기 위해 이 코드 조각 안에 설명을 작성했습니다.

const handleSubmit = async (e: React.FormEvent<htmlformelement>) => {
    e.preventDefault()

    if (!file) {
      alert('Please select a file to upload.')
      return
    }

    setUploading(true)

    const response = await fetch(
      process.env.NEXT_PUBLIC_BASE_URL + '/api/upload',
      {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({ filename: file.name, contentType: file.type }),
      }
    )

    if (response.ok) {
      const { url, fields } = await response.json()

      const formData = new FormData()
      Object.entries(fields).forEach(([key, value]) => {
        formData.append(key, value as string)
      })
      formData.append('file', file)

      const uploadResponse = await fetch(url, {
        method: 'POST',
        body: formData,
      })

      if (uploadResponse.ok) {
        alert('Upload successful!')
      } else {
        console.error('S3 Upload Error:', uploadResponse)
        alert('Upload failed.')
      }
    } else {
      alert('Failed to get pre-signed URL.')
    }

    setUploading(false)
  }
</htmlformelement>

API/업로드

api/upload/route.ts에는 아래 코드가 있습니다.

import { createPresignedPost } from '@aws-sdk/s3-presigned-post'
import { S3Client } from '@aws-sdk/client-s3'
import { v4 as uuidv4 } from 'uuid'

export async function POST(request: Request) {
  const { filename, contentType } = await request.json()

  try {
    const client = new S3Client({ region: process.env.AWS_REGION })
    const { url, fields } = await createPresignedPost(client, {
      Bucket: process.env.AWS_BUCKET_NAME,
      Key: uuidv4(),
      Conditions: [
        ['content-length-range', 0, 10485760], // up to 10 MB
        ['starts-with', '$Content-Type', contentType],
      ],
      Fields: {
        acl: 'public-read',
        'Content-Type': contentType,
      },
      Expires: 600, // Seconds before the presigned post expires. 3600 by default.
    })

    return Response.json({ url, fields })
  } catch (error) {
    return Response.json({ error: error.message })
  }
}

handleSubmit의 첫 번째 요청은 /api/upload였으며 콘텐츠 유형과 파일 이름을 페이로드로 전송했습니다. 아래와 같이 파싱됩니다:

const { filename, contentType } = await request.json()

다음 단계는 S3 클라이언트를 생성한 다음 URL과 필드를 반환하는 미리 서명된 게시물을 생성하는 것입니다. 이 URL을 사용하여 파일을 업로드합니다.

이 지식을 바탕으로 Documenso에서 업로드가 어떻게 작동하는지 분석하고 비교해 보겠습니다.

Documenso에 PDF 파일 업로드

type=file인 입력 요소부터 시작하겠습니다. Documenso에서는 코드가 다르게 구성됩니다. document-dropzone.tsx라는 파일에서 입력 요소를 찾을 수 있습니다.

<input>

<p classname="text-foreground mt-8 font-medium">{_(heading[type])}</p>

여기서 getInputProps는 useDropzone을 반환합니다. Documenso는 React-dropzone을 사용합니다.

import { useDropzone } from 'react-dropzone';

onDrop이 props.onDrop을 호출하면 upload-document.tsx에서 onFileDrop이라는 속성 값을 찾을 수 있습니다.

<documentdropzone classname="h-[min(400px,50vh)]" disabled disabledmessage="{disabledMessage}" ondrop="{onFileDrop}" ondroprejected="{onFileDropRejected}"></documentdropzone>

onFileDrop 함수에서 어떤 일이 일어나는지 살펴보겠습니다.

const onFileDrop = async (file: File) => {
    try {
      setIsLoading(true);

      const { type, data } = await putPdfFile(file);

      const { id: documentDataId } = await createDocumentData({
        type,
        data,
      });

      const { id } = await createDocument({
        title: file.name,
        documentDataId,
        teamId: team?.id,
      });

      void refreshLimits();

      toast({
        title: _(msg`Document uploaded`),
        description: _(msg`Your document has been uploaded successfully.`),
        duration: 5000,
      });

      analytics.capture('App: Document Uploaded', {
        userId: session?.user.id,
        documentId: id,
        timestamp: new Date().toISOString(),
      });

      router.push(`${formatDocumentsPath(team?.url)}/${id}/edit`);
    } catch (err) {
      const error = AppError.parseError(err);

      console.error(err);

      if (error.code === 'INVALID_DOCUMENT_FILE') {
        toast({
          title: _(msg`Invalid file`),
          description: _(msg`You cannot upload encrypted PDFs`),
          variant: 'destructive',
        });
      } else if (err instanceof TRPCClientError) {
        toast({
          title: _(msg`Error`),
          description: err.message,
          variant: 'destructive',
        });
      } else {
        toast({
          title: _(msg`Error`),
          description: _(msg`An error occurred while uploading your document.`),
          variant: 'destructive',
        });
      }
    } finally {
      setIsLoading(false);
    }
  };

많은 일이 일어나고 있지만 분석을 위해 putFile이라는 함수만 고려해 보겠습니다.

putPdf파일

putPdfFile은 upload/put-file.ts에 정의되어 있습니다

/**
 * Uploads a document file to the appropriate storage location and creates
 * a document data record.
 */
export const putPdfFile = async (file: File) => {
  const isEncryptedDocumentsAllowed = await getFlag('app_allow_encrypted_documents').catch(
    () => false,
  );

  const pdf = await PDFDocument.load(await file.arrayBuffer()).catch((e) => {
    console.error(`PDF upload parse error: ${e.message}`);

    throw new AppError('INVALID_DOCUMENT_FILE');
  });

  if (!isEncryptedDocumentsAllowed && pdf.isEncrypted) {
    throw new AppError('INVALID_DOCUMENT_FILE');
  }

  if (!file.name.endsWith('.pdf')) {
    file.name = `${file.name}.pdf`;
  }

  removeOptionalContentGroups(pdf);

  const bytes = await pdf.save();

  const { type, data } = await putFile(new File([bytes], file.name, { type: 'application/pdf' }));

  return await createDocumentData({ type, data });
};

putFile

putFile 함수를 호출합니다.

/**
 * Uploads a file to the appropriate storage location.
 */
export const putFile = async (file: File) => {
  const NEXT_PUBLIC_UPLOAD_TRANSPORT = env('NEXT_PUBLIC_UPLOAD_TRANSPORT');

  return await match(NEXT_PUBLIC_UPLOAD_TRANSPORT)
    .with('s3', async () => putFileInS3(file))
    .otherwise(async () => putFileInDatabase(file));
};

putFileInS3

const putFileInS3 = async (file: File) => {
  const { getPresignPostUrl } = await import('./server-actions');

  const { url, key } = await getPresignPostUrl(file.name, file.type);

  const body = await file.arrayBuffer();

  const reponse = await fetch(url, {
    method: 'PUT',
    headers: {
      'Content-Type': 'application/octet-stream',
    },
    body,
  });

  if (!reponse.ok) {
    throw new Error(
      `Failed to upload file "${file.name}", failed with status code ${reponse.status}`,
    );
  }

  return {
    type: DocumentDataType.S3_PATH,
    data: key,
  };
};

getPresignPostUrl

export const getPresignPostUrl = async (fileName: string, contentType: string) => {
  const client = getS3Client();

  const { getSignedUrl } = await import('@aws-sdk/s3-request-presigner');

  let token: JWT | null = null;

  try {
    const baseUrl = APP_BASE_URL() ?? 'http://localhost:3000';

    token = await getToken({
      req: new NextRequest(baseUrl, {
        headers: headers(),
      }),
    });
  } catch (err) {
    // Non server-component environment
  }

  // Get the basename and extension for the file
  const { name, ext } = path.parse(fileName);

  let key = `${alphaid(12)}/${slugify(name)}${ext}`;

  if (token) {
    key = `${token.id}/${key}`;
  }

  const putObjectCommand = new PutObjectCommand({
    Bucket: process.env.NEXT_PRIVATE_UPLOAD_BUCKET,
    Key: key,
    ContentType: contentType,
  });

  const url = await getSignedUrl(client, putObjectCommand, {
    expiresIn: ONE_HOUR / ONE_SECOND,
  });

  return { key, url };
};

비교

  • Documenso에 POST 요청이 표시되지 않습니다. URL을 가져오기 위해 getSignedUrl이라는 함수를 사용하는 반면,

    vercel 예제는 API/업로드 경로에 대한 POST 요청을 만듭니다.

  • 입력 요소는 Vercel 예제에서 예시일 뿐이므로 쉽게 찾을 수 있지만 Documenso는 발견됩니다

    React-dropzone을 사용하고 입력 요소가 비즈니스 컨텍스트에 따라 위치합니다.

회사 소개:

Thinkthroo에서는 대규모 오픈소스 프로젝트를 연구하고 아키텍처 가이드를 제공합니다. 우리는 귀하의 프로젝트에서 사용할 수 있는 tailwind로 구축된 재사용 가능한 구성요소를 개발했습니다.

Next.js, React, Node 개발 서비스를 제공합니다.

귀하의 프로젝트에 대해 논의하려면 회의를 예약하세요.

Comparison of Spload feature between Documenso and aws-smage-upload example

참고자료:

  1. https://github.com/documenso/documenso/blob/main/packages/lib/universal/upload/put-file.ts#L69

  2. https://github.com/vercel/examples/blob/main/solutions/aws-s3-image-upload/README.md

  3. https://github.com/vercel/examples/tree/main/solutions/aws-s3-image-upload

  4. https://github.com/vercel/examples/blob/main/solutions/aws-s3-image-upload/app/page.tsx#L58C5-L76C12

  5. https://github.com/vercel/examples/blob/main/solutions/aws-s3-image-upload/app/api/upload/route.ts

  6. https://github.com/documenso/documenso/blob/main/packages/ui/primitives/document-dropzone.tsx#L157

  7. https://react-dropzone.js.org/

  8. https://github.com/documenso/documenso/blob/main/apps/web/src/app/(dashboard)/documents/upload-document.tsx#L61

  9. https://github.com/documenso/documenso/blob/main/packages/lib/universal/upload/put-file.ts#L22

위 내용은 Documenso와 aws-smage-upload 예제의 Spload 기능 비교의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
관련 기사
JavaScript 데이터 유형 : 브라우저와 Nodejs 사이에 차이가 있습니까?JavaScript 데이터 유형 : 브라우저와 Nodejs 사이에 차이가 있습니까?May 14, 2025 am 12:15 AM

JavaScript 코어 데이터 유형은 브라우저 및 Node.js에서 일관되지만 추가 유형과 다르게 처리됩니다. 1) 글로벌 객체는 브라우저의 창이고 node.js의 글로벌입니다. 2) 이진 데이터를 처리하는 데 사용되는 Node.js의 고유 버퍼 객체. 3) 성능 및 시간 처리에는 차이가 있으며 환경에 따라 코드를 조정해야합니다.

JavaScript 댓글 : / / * * /사용 안내서JavaScript 댓글 : / / * * /사용 안내서May 13, 2025 pm 03:49 PM

javaScriptUSTWOTYPESOFSOFCOMMENTS : 단일 라인 (//) 및 multi-line (//)

Python vs. JavaScript : 개발자를위한 비교 분석Python vs. JavaScript : 개발자를위한 비교 분석May 09, 2025 am 12:22 AM

Python과 JavaScript의 주요 차이점은 유형 시스템 및 응용 프로그램 시나리오입니다. 1. Python은 과학 컴퓨팅 및 데이터 분석에 적합한 동적 유형을 사용합니다. 2. JavaScript는 약한 유형을 채택하며 프론트 엔드 및 풀 스택 개발에 널리 사용됩니다. 두 사람은 비동기 프로그래밍 및 성능 최적화에서 고유 한 장점을 가지고 있으며 선택할 때 프로젝트 요구 사항에 따라 결정해야합니다.

Python vs. JavaScript : 작업에 적합한 도구 선택Python vs. JavaScript : 작업에 적합한 도구 선택May 08, 2025 am 12:10 AM

Python 또는 JavaScript를 선택할지 여부는 프로젝트 유형에 따라 다릅니다. 1) 데이터 과학 및 자동화 작업을 위해 Python을 선택하십시오. 2) 프론트 엔드 및 풀 스택 개발을 위해 JavaScript를 선택하십시오. Python은 데이터 처리 및 자동화 분야에서 강력한 라이브러리에 선호되는 반면 JavaScript는 웹 상호 작용 및 전체 스택 개발의 장점에 없어서는 안될 필수입니다.

파이썬 및 자바 스크립트 : 각각의 강점을 이해합니다파이썬 및 자바 스크립트 : 각각의 강점을 이해합니다May 06, 2025 am 12:15 AM

파이썬과 자바 스크립트는 각각 고유 한 장점이 있으며 선택은 프로젝트 요구와 개인 선호도에 따라 다릅니다. 1. Python은 간결한 구문으로 데이터 과학 및 백엔드 개발에 적합하지만 실행 속도가 느립니다. 2. JavaScript는 프론트 엔드 개발의 모든 곳에 있으며 강력한 비동기 프로그래밍 기능을 가지고 있습니다. node.js는 풀 스택 개발에 적합하지만 구문은 복잡하고 오류가 발생할 수 있습니다.

JavaScript의 핵심 : C 또는 C에 구축 되었습니까?JavaScript의 핵심 : C 또는 C에 구축 되었습니까?May 05, 2025 am 12:07 AM

javaScriptisNotBuiltoncorc; it'SangretedLanguageThatrunsonOngineStenWrittenInc .1) javaScriptWasDesignEdasAlightweight, 해석 hanguageforwebbrowsers.2) Endinesevolvedfromsimpleplemporectreterstoccilpilers, 전기적으로 개선된다.

JavaScript 응용 프로그램 : 프론트 엔드에서 백엔드까지JavaScript 응용 프로그램 : 프론트 엔드에서 백엔드까지May 04, 2025 am 12:12 AM

JavaScript는 프론트 엔드 및 백엔드 개발에 사용할 수 있습니다. 프론트 엔드는 DOM 작업을 통해 사용자 경험을 향상시키고 백엔드는 Node.js를 통해 서버 작업을 처리합니다. 1. 프론트 엔드 예 : 웹 페이지 텍스트의 내용을 변경하십시오. 2. 백엔드 예제 : node.js 서버를 만듭니다.

Python vs. JavaScript : 어떤 언어를 배워야합니까?Python vs. JavaScript : 어떤 언어를 배워야합니까?May 03, 2025 am 12:10 AM

Python 또는 JavaScript는 경력 개발, 학습 곡선 및 생태계를 기반으로해야합니다. 1) 경력 개발 : Python은 데이터 과학 및 백엔드 개발에 적합한 반면 JavaScript는 프론트 엔드 및 풀 스택 개발에 적합합니다. 2) 학습 곡선 : Python 구문은 간결하며 초보자에게 적합합니다. JavaScript Syntax는 유연합니다. 3) 생태계 : Python에는 풍부한 과학 컴퓨팅 라이브러리가 있으며 JavaScript는 강력한 프론트 엔드 프레임 워크를 가지고 있습니다.

See all articles

핫 AI 도구

Undresser.AI Undress

Undresser.AI Undress

사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover

AI Clothes Remover

사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool

Undress AI Tool

무료로 이미지를 벗다

Clothoff.io

Clothoff.io

AI 옷 제거제

Video Face Swap

Video Face Swap

완전히 무료인 AI 얼굴 교환 도구를 사용하여 모든 비디오의 얼굴을 쉽게 바꾸세요!

뜨거운 도구

PhpStorm 맥 버전

PhpStorm 맥 버전

최신(2018.2.1) 전문 PHP 통합 개발 도구

드림위버 CS6

드림위버 CS6

시각적 웹 개발 도구

ZendStudio 13.5.1 맥

ZendStudio 13.5.1 맥

강력한 PHP 통합 개발 환경

VSCode Windows 64비트 다운로드

VSCode Windows 64비트 다운로드

Microsoft에서 출시한 강력한 무료 IDE 편집기

WebStorm Mac 버전

WebStorm Mac 버전

유용한 JavaScript 개발 도구