Next.js 是基于 React 的服务端渲染框架。它提供了很多便利的功能,比如代码分割、预测性路由、数据预取等,使得我们能够快速构建高性能的 Web 应用。然而,在面对一些静态资源(如图片、音视频等)的上传和管理时,Next.js 并没有提供很好的解决方案,本文将介绍一种可行的方法。
问题
在 Next.js 中,我们通常使用 next/image
组件来引入图片。它可以自动根据屏幕的分辨率和当前网速,生成适合的图片大小和质量,从而提升用户体验。然而,当你需要上传一些大型静态资源时,如何让它们能够被正确地加载呢?我们考虑将它们存放在一个 CDN 中,让它充分发挥出 “分布式静态资源服务” 的优势。但一方面,Next.js 并没有直接的 API 支持 CDN 的上传,另一方面,如果采用传统的上传方式,很可能会遇到以下问题:
- 上传的过程耗时较长,会阻塞渲染线程,导致页面卡顿。
- 如果上传的资源过大,可能会导致某些用户的上传失败或上传速度较慢。而用户是无法得知哪些资源上传成功、失败或正在上传的。
- CDN 的上传接口可能会受到一些限制,如单个文件大小、同时上传个数、上传间隔等,我们需要考虑这些限制如何优雅地处理。
解决方案
我们可以采用一种类似于“分片上传”的方式,将大型静态资源切割成若干个小块,每个小块一个个地上传到 CDN 上,上传成功后将其地址保存起来。而在下载时,只需将多个小块链接拼接起来即可,如下图所示:
这种方式能够避免同时上传过多文件所导致的资源浪费;对于上传失败的小块,我们可以对其进行重传,而不需要将整个大块重新上传。除此之外,在上传过程中,我们还可以通过 WebSocket、Server-Sent Events 等技术实现实时进度更新等功能。
接下来,我们将会基于 Next.js 和 Uppy 这个优秀的文件上传库,实现一个 “分片上传” 的示例。项目地址:ice-raven/nextjs-upload-demo。
实现步骤
集成 Uppy
首先,我们需要引入 Uppy 并配置它。我们可以通过 npm
或 yarn
安装:
npm install uppy --save
我们需要创建一个 Uppy
对象来管理上传功能;并使用 Uppy
的插件 XHRUpload
对文件进行上传。下面是一个简单的配置:
-- -------------------- ---- ------- ------ ---- ---- ------------- ------ --------- ---- ------------------- ----- ---- - ------ ------------ ----- --- ------------------- - --------- ----------------------------- ---
分割文件
我们需要将文件分割成固定大小的小块。这里我们使用 File.slice()
方法进行分割。
-- -------------------- ---- ------- -------- --------------- ---- - ----- - --- ----- - -- --- --- - ----- ----- ------ - --- ----- ------ - ---------- - ----------------------------- ------ ----- - ---- --- - ----- - ----- - ------ ------- -
上传小块
接下来,我们需要使用 Uppy 的 API 对每个小块进行上传,并将上传成功后返回的文件地址进行保存。
-- -------------------- ---- ------- -------- ------------------ - ----- ------ - ---------------- ----- -------- - ------------------ ------ -- - ------ -------------------------------- -- - ------ - ------ --------- ----------- -- --- --- ------ ---------------------- -
这里的 response
是上传成功后,CDN 返回的 JSON 数据格式。我们需要从中提取出文件的地址并返回,如下:
function extractResponse(response) { const fileName = response.upload_data.filename; const id = response.upload_data.batch_id; return `${CDN_BASE}/${id}/${fileName}`; }
拼接文件
最后,我们需要将文件中的小块地址拼接成一个完整的 URL。
function concatFile(chunks) { const blob = new Blob(chunks); return URL.createObjectURL(blob); }
完整示例代码
下面是一个完整的 Next.js 页面,它可以上传文件并显示出上传后的图片。
-- -------------------- ---- ------- ------ - -------- - ---- -------- ------ ---- ---- ------------- ------ --------- ---- ------------------- ----- ---- - ------ ------------ ----- --- ------------------- - --------- ----------------------------- --- ----- -------- - -------------------------- -------- --------------- ---- - ----- - --- ----- - -- --- --- - ----- ----- ------ - --- ----- ------ - ---------- - ----------------------------- ------ ----- - ---- --- - ----- - ----- - ------ ------- - -------- ------------------------- - ----- -------- - ------------------------------ ----- -- - ------------------------------ ------ -------------------------------- - -------- ------------------ - ----- ------ - ---------------- ----- -------- - ------------------ ------ -- - ------ -------------------------------- -- - ------ - ------ --------- ----------- -- --- --- ------ ------------------------------------ -- - ---------------- -- -- ------- - --------- ------ --------------- -- ----------------------------- --- - -------- ------------------ - ----- ---- - --- ------------- ------ -------------------------- - ------ ------- -------- ---------- - ----- --------- ----------- - ------------- ----- ---------------- - ----- --- -- - ----- ---- - ------------------ -- ------- ------- ----- ---- - ----- ------------------- ----------------------------- -- ------ - ----- ------ ----------- --------------------------- -- -------- -- ---- ------------- ------------ --- ------ -- -
总结
通过上述示例,我们可以看出 “分片上传” 的方式确实能够解决上传大型静态资源的问题,并且对于后续的管理和维护也有很大的帮助。当然,在实际项目中,还需要考虑诸如安全、CDN 版本控制、错误处理等问题。希望本文能够为大家提供一些思路和参考,使得我们在 Next.js 中上传大型静态资源更加得心应手。
来源:JavaScript中文网 ,转载请注明来源 https://www.javascriptcn.com/post/6461860a968c7c53b02e5af8