NextJS和Koa的文件上传&下载以及二进制加密&解密

2 个月前(已编辑)
/ ,
26

阅读此文章之前,你可能需要首先阅读以下的文章才能更好的理解上下文。

最近,我的毕设来到了对容器的文件管理的需求。而在管理文件中,最让人头大的则是文件的上传与下载了。

首先先回顾一下项目的整体架构吧,Next 提供 ssr 渲染出的前端页面&handle API 请求;基于 Koa 的 Daemon 部署在各个可以公网访问到的 VPS 上,作为容器运行的环境。Daemon 提供 Http API 供 Next 调用。目前 Daemon 会有一个固定的 token,作为与 Next 通信的对称加密的密钥。因为部署 Next 的机器肯定有 https,而 Daemon 所在的节点可能没有,所以这第二程(Next->Koa)数据是一定需要加密的,而第一程则不需要。

所以一个请求的流程是,客户在浏览器发起请求到 Next route handler,Next 鉴权无误后利用 token 加密请求到 Daemon,Daemon 解密后执行请求的操作,后返回数据给 Next(同样有加解密),然后 Next 再把请求给客户。

文件上传到 Next

首先第一步是将文件上传至 Next,前端方面我直接使用 Antd 的<Upload />组件,并通过自定义请求customRequest附带上想要操作的实例和目标路径到 params。请求体使用 FormData:

在 Next 端,我使用的是 13 的 APP router,提供了解析 FormData 的方法:

阅读File 的文档可知,File 其实是继承自Blob,而 Blob 的方法arrayBuffer()则会返回一个 promise,其会兑现一个包含 Blob 所有内容的二进制格式的 ArrayBuffer。

Buffer.from()则是 node 的方法,它将一个 ArrayBuffer 以 Uint8Array 方式读取出来,这时候如果我们使用console.log(buffer.toString())的话就可以在终端打印出来上传的文件的内容了(如果上传的是文本文件的话)。

加解密二进制

下一步就是把文件从 Next 加密后发去 Daemon,所以接下来就要研究如何加解密二进制了。

CryptoJS 支持加密WordArray类型的二进制数据,而这个 WordArray 其实就是存着 32-bit 数的数组,大概长这样:[0x00010203, 0x04050607]。 如何将我们的 ArrayBuffer 转为 WordArray,我参考了这篇文章所提供的方法:

那么,就可以进行一个加解密测试了:

经测试,上传的文本文件成功打印出解密的内容,上传的图片文件成功写入到指定位置且正常打开!二进制的加解密工作完成!

改写 axios 和 koa 中间件

上面的工作只是在 Next 本地测试了一下,要实际使用,需要改写 next 和 koa 的中间件。

对于请求,把 ArrayBuffer 放在 contentBuffer 字段里:

然后在请求的中间件中,将contentBuffer抽离出来单独加密,再在其他内容加密完成后再塞进去。

对于 Koa 的中间件,也要相应的进行处理:

然后在 Koa 的相应路由中写上相应的把 buffer 写入对应地址的文件中,文件的上传就大功告成啦!
文件的下载也同理,这个就之后在写啦。

可以优化的地方

至此,虽然文件的上传可以正确无误的达到目标了,但是却代价巨大。为何?因为我们在 Next 直接把文件从客户端拿了上来并存在了 buffer 里,然后把这个 buffer 整个又发给了 koa,在测试中我们还发生了“413request entity too large”的问题,通过改 bodyParser 的配置解决了。但是 buffer 这种整存整取的方式对内存的消耗实在是太大了!试想,如果用户要把一个 10G 的压缩包(比如说地图很大的服务端)传上来,传输过程中 Next 和 Daemon 都要占用 10G 的内存!!!!这显然是代价巨大难以接受的。

所以如果我完成毕设的基本功能后如果还有时间精力,需要对这个点进行一下优化,改为使用浏览器到 Next 相同的“流式传输”来节省内存。那么流式传输的对称加密也是在优化中要攻克的一个知识点了,想必届时我还会再写一篇文章罢!


240103 更新:上面这个流式传输的 Feature 搞定了,详见:https://yuzi.dev/posts/frontend/stream-upload

评论区加载中...