首页 web前端 js教程 Documenso 和 aws-smage-upload 示例之间的 Spload 功能比较

Documenso 和 aws-smage-upload 示例之间的 Spload 功能比较

Jan 03, 2025 am 01:41 AM

在本文中,我们将比较 Documenso 和 AWS S3 图像上传示例之间将文件上传到 AWS S3 所涉及的步骤。

我们从 Vercel 提供的简单示例开始。

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

示例/aws-s3-image-upload

Vercel 提供了一个将文件上传到 AWS S3 的良好示例。

此示例的自述文件提供了两个选项,您可以使用现有的 S3 存储桶或创建新存储桶。了解这一点有帮助

您正确配置了上传功能。

又到了我们看源码的时间了。我们正在寻找 type=file 的输入元素。在 app/page.tsx 中,您将找到以下代码:

return (
    <main>
      <h1>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 函数中发生了很多事情。我们需要分析这个handleSubmit函数中的操作列表。我已在此代码片段中编写了注释来解释这些步骤。

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)
  }
登录后复制

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 和字段的预签名帖子。您将使用此网址上传您的文件。

有了这些知识,我们来分析一下Documenso中的上传工作原理并进行一些比较。

在 Documenso 中上传 PDF 文件

让我们从 type=file 的输入元素开始。 Documenso 中的代码组织方式不同。您会在名为 document-dropzone.tsx.
的文件中找到输入元素

<input {...getInputProps()} />

<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={remaining.documents === 0 || !session?.user.emailVerified}
  disabledMessage={disabledMessage}
  onDrop={onFileDrop}
  onDropRejected={onFileDropRejected}
/>
登录后复制

让我们看看 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 函数。

/**
 * 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 请求。它使用名为 getSignedUrl 的函数来获取 url,而

    vercel 示例向 api/upload 路由发出 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

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

Video Face Swap

Video Face Swap

使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热门文章

<🎜>:泡泡胶模拟器无穷大 - 如何获取和使用皇家钥匙
4 周前 By 尊渡假赌尊渡假赌尊渡假赌
北端:融合系统,解释
4 周前 By 尊渡假赌尊渡假赌尊渡假赌
Mandragora:巫婆树的耳语 - 如何解锁抓钩
3 周前 By 尊渡假赌尊渡假赌尊渡假赌

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

热门话题

Java教程
1671
14
CakePHP 教程
1428
52
Laravel 教程
1331
25
PHP教程
1276
29
C# 教程
1256
24
Python vs. JavaScript:学习曲线和易用性 Python vs. JavaScript:学习曲线和易用性 Apr 16, 2025 am 12:12 AM

Python更适合初学者,学习曲线平缓,语法简洁;JavaScript适合前端开发,学习曲线较陡,语法灵活。1.Python语法直观,适用于数据科学和后端开发。2.JavaScript灵活,广泛用于前端和服务器端编程。

从C/C到JavaScript:所有工作方式 从C/C到JavaScript:所有工作方式 Apr 14, 2025 am 12:05 AM

从C/C 转向JavaScript需要适应动态类型、垃圾回收和异步编程等特点。1)C/C 是静态类型语言,需手动管理内存,而JavaScript是动态类型,垃圾回收自动处理。2)C/C 需编译成机器码,JavaScript则为解释型语言。3)JavaScript引入闭包、原型链和Promise等概念,增强了灵活性和异步编程能力。

JavaScript和Web:核心功能和用例 JavaScript和Web:核心功能和用例 Apr 18, 2025 am 12:19 AM

JavaScript在Web开发中的主要用途包括客户端交互、表单验证和异步通信。1)通过DOM操作实现动态内容更新和用户交互;2)在用户提交数据前进行客户端验证,提高用户体验;3)通过AJAX技术实现与服务器的无刷新通信。

JavaScript在行动中:现实世界中的示例和项目 JavaScript在行动中:现实世界中的示例和项目 Apr 19, 2025 am 12:13 AM

JavaScript在现实世界中的应用包括前端和后端开发。1)通过构建TODO列表应用展示前端应用,涉及DOM操作和事件处理。2)通过Node.js和Express构建RESTfulAPI展示后端应用。

了解JavaScript引擎:实施详细信息 了解JavaScript引擎:实施详细信息 Apr 17, 2025 am 12:05 AM

理解JavaScript引擎内部工作原理对开发者重要,因为它能帮助编写更高效的代码并理解性能瓶颈和优化策略。1)引擎的工作流程包括解析、编译和执行三个阶段;2)执行过程中,引擎会进行动态优化,如内联缓存和隐藏类;3)最佳实践包括避免全局变量、优化循环、使用const和let,以及避免过度使用闭包。

Python vs. JavaScript:社区,图书馆和资源 Python vs. JavaScript:社区,图书馆和资源 Apr 15, 2025 am 12:16 AM

Python和JavaScript在社区、库和资源方面的对比各有优劣。1)Python社区友好,适合初学者,但前端开发资源不如JavaScript丰富。2)Python在数据科学和机器学习库方面强大,JavaScript则在前端开发库和框架上更胜一筹。3)两者的学习资源都丰富,但Python适合从官方文档开始,JavaScript则以MDNWebDocs为佳。选择应基于项目需求和个人兴趣。

Python vs. JavaScript:开发环境和工具 Python vs. JavaScript:开发环境和工具 Apr 26, 2025 am 12:09 AM

Python和JavaScript在开发环境上的选择都很重要。1)Python的开发环境包括PyCharm、JupyterNotebook和Anaconda,适合数据科学和快速原型开发。2)JavaScript的开发环境包括Node.js、VSCode和Webpack,适用于前端和后端开发。根据项目需求选择合适的工具可以提高开发效率和项目成功率。

C/C在JavaScript口译员和编译器中的作用 C/C在JavaScript口译员和编译器中的作用 Apr 20, 2025 am 12:01 AM

C和C 在JavaScript引擎中扮演了至关重要的角色,主要用于实现解释器和JIT编译器。 1)C 用于解析JavaScript源码并生成抽象语法树。 2)C 负责生成和执行字节码。 3)C 实现JIT编译器,在运行时优化和编译热点代码,显着提高JavaScript的执行效率。

See all articles