最近一个项目要用到大文件的断点续传,多大文件呢,大的包可能会有 20G ,目前的做法是
前端取到文件后,按 2m 一个片段进行分片,然后逐个上传 后端收到完整的文件后放在一个隐藏的目录内,等最后的文件传完之后,逐一合并,并移动到指定的文件夹
实现是实现了,最大的问题不是上传,而是合并以及转移文件相当消耗时间
有没有更高级的做法呢?
1
vivisidea 122 天前
是需要完全要自己实现么?你们项目有没有用对象存储?对象存储 sdk 一般都支持这个分片上传+合并操作的
https://aws.amazon.com/cn/blogs/compute/uploading-large-objects-to-amazon-s3-using-multipart-upload-and-transfer-acceleration/ |
2
mx1700 122 天前 via Android 6
移动如果同一硬盘不应该耗时啊
合并的方式不太对,应该直接创建完整大小的空文件,上传接口拿到的缓冲数据直接写到完整文件的指定位置,不需要合并操作 |
4
misoomang 122 天前
开源的对象存储 minio 是否可以考虑使用搭建
|
5
InDom 121 天前 1
|
6
Ipsum 121 天前 7
为什么不能直接按全文件大小在指定位置建立空文件,然后 offset 写实际数据进去呢?设置个 cron 。超过 3 天没传完自动删除。
|
7
MoHen9 121 天前 via Android 4
分成很多个小文件?说明你实现的断点续传不对,断点续传是一个大文件分成多个请求,每个请求只传自己分好的那一段数据,比如 20 个 G 分成 20 个请求,每个请求传 1G ,第一个请求传从 0 到 1G ,第二个请求从 1G 到 2G ,以依次类推,服务器也是一个文件,根据请求写入文件对应的位置。请求头会携带一些分段的信息,这个有标准的 header ,好像是 range ,可以搜一下
|
8
scegg 121 天前
1 建立一个临时文件储存区。
2 上传文件方法分为 ( 1 )新建临时文件,body 是文件的第一段,post 成功后将数据保存在临时文件区,文件名可以为一个随机 guid ,返回这个 id 。 ( 2 )附加临时文件,body 是文件的第 N 段,将方法 1 返回的 id 作为 query 参数一并提供,post 成功后将新提供的数据附加在这个 id 指定的文件后。 3 在原“使用”文件的位置(例如新建文件的方法),增加临时文件 id 参数。将指定临时文件移动到永久存储区。 客户端流程: 1 打开一个文件。 使用“新建临时文件”方法,传送第一块,得到文件 ID ;使用“附加临时文件”方法,并提交文件 ID ,传送后续的块;使用原业务方法,提交文件 ID ,完成从临时文件到永久存储区的转移和使用。 |
9
humbass OP |
12
rekulas 121 天前 1
移动文件应该很快,耗时应该是在合并上,增加了额外的 io 时间
所以最佳方案应该就是预申请空间,然后不同的线程可以在不同的段进行 io 写入不会冲突(如果是单线程写入那就更不会了), 只需要验证分块 hash 正确最后的文件应该就没啥问题了 |
13
renmu 121 天前 via Android
建议检查代码的实现,理论上不会很耗时
|
14
jiangzm 121 天前
为啥一定要按分片存储呢, 直接将缓冲不断写入单文件不好吗? 还是说用缓存文件替代缓冲,不管是处理缓冲区(Buffer)还是缓存文件,其实都需要每收到一次请求及时做写入目标文件处理啊。
|
16
Suaxi 121 天前 via Android
领导允许用开源对象存储的话,可以参考一下 minio 的分片上传
|
17
salparadise 121 天前
做过类似断点续传,用的 Oss 分片+合并
|
18
01802 121 天前 via Android
用 syncthing 去传也行,可以自建
|
19
guanzhangzhang 121 天前
创建大小文件,http range 和你 server 进程 seek 写就行
|
21
expy 121 天前
预分配一个完整文件,用分块序号和分块大小计算要写入的偏移量,前端上传前先查询,后端返回未上传的分块序号。
|
22
listen2wind 121 天前
@humbass #3 minio 是可以的,我们使用过,部署在内网的。
|
23
jorneyr 121 天前
4G 左右的文件计算 MD 需要 13+S ,这个耗时前后端都不可避免,需要验证文件的完整性是有必要的。
至于后端小文件合并成大文件,可以使用一个线程合并,也可以使用多个线程分部分合并,然后继续往上合并。 后端也可以使用文件内存映射直接写入文件中对应的 fragment ,不把收到的部分写入小文件。 具体要分析是哪个部分慢,例如是计算 MD5 32 慢,看看是否改成 MD5 16 也能够满足需求。 |
24
lerry 121 天前
有轮子就直接用。
minio 是 golang 实现的,很容易部署,我是用的 docker ,直接下载一个单文件应该也可以。 |
25
TOUJOURSER 121 天前 1
正好前段时间遇到类似的需求,我们采用的是 tus + minio
|
26
shenyansycn 121 天前
放硬盘里,人肉过去传输,最快。
|
27
unknown404 121 天前
请使用支持 S3 的相关存储(公有云对象存储或者私有云 minio),建议 op 好好重读下 http 的 RFC ,https://datatracker.ietf.org/doc/html/rfc7233#section-4.2 有标准的协议不用,你去造轮子
|
29
MoYi123 121 天前
@unknown404 这是下载, 不是上传.
|
30
xingjue 121 天前
minio
|
31
v2tudnew 121 天前
去看看 BT 软件是如何实现就完事了,只是把一对多改成一对一。
|
32
unknown404 121 天前
@MoYi123 原理一样的
|
33
duglik 121 天前 1
|
34
heiya 121 天前
我的做法是:
1.前端做好分片,为每一个分片生成序号,统计分片的个数。将这些数据传给后端,后端把这些数据记录下来,为这个文件生成一个全局 id ,返回给前端。 2.前端每次上传分片会连带文件 id+分片 id 一起传过来。分片文件会被上传到一个临时目录。每上传完成一个分片,后端会记录下来分片 id (分片序号)。如果是并发上传的话,要注意已上传分片 id 集合会有线程安全问题,不然会出现某个分片已上传但没记录的问题。 3.后端返回已上传分片集合。同时会有一个异步线程判断该文件 id 下的分片是否全都上传完毕。如果全都上传完成,调用文件系统 SDK 的合并文件方法(我用的是 minio ),合并完成之后,删除临时文件目录的分片。 4.与此同时,前端全部分片上传完成之后,循环调用获取文件合并状态接口。 完成~ |
35
klo424 121 天前 1
@humbass #15
1. 前端生成一个文件 id ,连带文件 size 、offset 和二进制数据传给后端。 2. 后端生成一个 size 大小的空文件,根据 id 找到文件,再根据 offset 去寻找位置写入二进制数据。 3. 后端写完数据返回成功,前端 offset 更新继续传下一个二进制数据给后端,以此循环。 4. 如果断了,下次打开页面时,从后端获取到未完成的文件 id ,再执行 1-3 过程。 |
36
cstj0505 121 天前
其实分片这东西完全是浏览器限制,不用 js,用个流式写入大文件直接边读边写
|
37
yaodong0126 121 天前
好家伙,你们但凡看过断点续传的原理,就说不出什么转移合并的话
|
38
trzzzz 121 天前 1
@heiya 透传 minio 的 uploadId 给前端,前端每次上传分片就带上 uploadId 和 partNumber 。服务器收到一片也不用写本地,直接透传到 minio ,我习惯这样。
但这样在弱网环境下会有问题,简单说个 流的 retry 需要自己写,这个是最麻烦的。如果是弱网还是建议先写 tmp 再异步上传 |
40
ZZ74 121 天前
@klo424
复议 这才是断点续传的最初或者真实的做法。 OP 可以用这种方式避免重新组合文件。java 这边对应的 radomaccessfile.... 另外为啥要组合?如果服务器不需要读取处理啥的,只是提供存储,直接存分片,下载的时候由前端拼也可以。 我不懂前端,你这 20G 的文件 前端下载时是否合并的了得打个问号 |
41
jiangzm 121 天前
@humbass #15 不需要告诉客户端怎么传,而是客户端怎么传告诉服务端。根据分片的大小,分片的索引可以计算 offset ,有 offset 不就可以直接写入了。 你最后上传完合并不也要确定分片的顺序吗, 在等待的过程完全可以边写边传。
|
42
netnr 121 天前 2
|
43
okakuyang 121 天前 via iPhone
这些不都是基操吗,检查下自己代码,怕不是有 bug
|
44
hetal 120 天前
rsync 不行么?
|
45
zhaokun 120 天前
内网搭建一台开源的对象存储
|
46
zhaokun 120 天前
为啥要等都上传完了再合并,可以上传一点合并一点呀,顺序不对的就等着呗,合并完再移?不是直接在目标文件夹下直接合并吗?
|
48
victimsss 118 天前
--- 因为是企业内网网络(含广义上的内网)使用,不能使用第三方的云端存储。
第三方和内不内网有什么关系吗,贵司平时不是用支持私有部署的服务吗 |
49
heiya 118 天前
@trzzzz hi ,我还是没 get 到弱网环境下的问题。实际在上传时后端会维护一个 uploadId 下的已上传分片数组,客户端(我这边是 windows 客户端)会根据这个数组尝试重试机制。所以无论网络环境如何、是否是并发上传,只要客户端能正确处理这个数组就可以。
|
52
dode 117 天前
分块上传,并且要求按顺序上传,服务器顺序写入追加就行了
|
53
dode 117 天前
客户端按固定大小分块上传,直至文件结束
|