大家好,感谢邀请,今天来为大家分享一下多文件并行上传方案设计的问题,以及和的一些困惑,大家要是还不太明白的话,也没有关系,因为接下来将为大家分享,希望可以帮助到大家,解决大家的问题,下面就开始吧!
抖音、快手等短视频应用都具有本地编辑视频并上传的功能。这里的上传是指上传视频文件。其实无论是上传视频还是其他文件,技术原理都是一样的。
02
方案设计
1. 任务管理
下图展示了上传的整体逻辑。上传逻辑分为三个部分。核心上传逻辑由UploadManager实现,其他都是面向业务的:
每个视频文件上传都将作为一个任务进行处理。上传之前会判断是否秒上传,即时上传的逻辑后面会详细讲解;所有任务均由uploadTasks 管理。添加新任务时,会判断是否有任务正在上传。如果没有要上传的任务,则先将任务添加到uploadTasks中,然后进入上传流程。如果有任务正在上传,则该任务会被添加到uploadTasks中,并在队列中等待上一个任务上传。然后就进入分片上传的逻辑了。所有分段上传完成后,将自动上传下一个文件。需要注意的是,删除视频时,需要调用该接口通知服务器文件id被丢弃,上传的文件被删除。
2. 第二次传输逻辑
由于视频文件较大,占用服务器存储空间,且不同CDN节点上有多个相同的备份,占用较多存储空间。本质上,同一个文件只需要在服务器上存在一份副本,因此针对这个问题,我们设计了一套复制逻辑。
在获取上传地址之前,我们会计算一个视频文件的md5,并通过上传地址接口传给服务器,服务器会进行比较。如果存在相同文件,则直接使用即时上传逻辑,并下发文件ID。客户端只会通过文件ID更新视频信息,不会上传视频文件。这样就从业务层完成了上传过程。
3. 分段上传
这里我们首先需要区分任务和分片的概念。每个文件对应一个任务,一个文件会被切分成多个分片进行上传。上传方式为表单提交。具体流程如下:
上传地址是服务器动态下发的,不是固定地址。由于上传地址会过期,无论是首次上传还是继续上传,都需要在开始上传之前请求上传地址。如果是第一次上传,请求upload.do接口获取上传地址和文件id。如果继续,则上传请求resume.do接口获取续传地址。由于续传不是第一次上传,所以会有文件ID,需要将文件ID带到服务器;然后进入文件切片阶段,文件切片是通过handle实现的。从前向后求对应的大小,截取对应的字节,切片后拼接表单参数;初始化待上传数组,数组中存储的元素为待上传索引。上传过程中,会从上传数组中取出要上传的索引。一部分一部分上传。上传成功后,分片会被插入到finish数组中,表示上传已完成;上传过程是并行的,并发数不是固定值,而是连续动态计算的。上传模块具有测速逻辑,根据测速结果动态改变分片并发上传数;发生错误时,如果由于网络抖动或服务器原因导致上传失败,会根据相应情况选择是否重试,单任务最大重试次数为3次;当任务对应的所有分片都上传完毕后,会请求上传地址,并发送一个特殊的标识符通知服务器。当前任务文件上传完成。 03
表单提交
该表格处理起来相对复杂,并且都遵循标准格式。大致格式如下:
–boundary Content-Disposition: 表单数据; name=’参数名称’ 参数值–boundary Content-Disposition:form-data;name=”表单控件名称”;filename=”上传文件名” Content-Type:mime type 上传文件二进制数据–boundary–代码实现逻辑如下。代码已经脱敏,iOS项目改成Boundary就可以直接使用了。
需要在表格的开头和结尾添加边界,以指示文件的边界。不同参数之间还需要添加边界。这是固定格式。代码中有一些换行符。这些换行符的格式是固定的,不能增加或减少。
– (NSString *)writeMultipartFormData:(NSData *)数据参数:(NSDictionary *)参数{ if (data.length==0) { return nil; } NSMutableData *formData=[NSMutableData 数据]; NSData *lineData=[@’\r\n’ dataUsingEncoding:NSUTF8StringEncoding]; NSString *boundaryString=[NSString stringWithFormat:@’–%@’, 边界]; NSData *boundary=[boundaryString dataUsingEncoding:NSUTF8StringEncoding]; //拼接上传参数[parameters enumerateKeysAndObjectsUsingBlock:^(id key , id obj, BOOL *stop) { [formDataappendData:boundary]; [formData追加数据:lineData]; NSString *thisFieldString=[NSString stringWithFormat: @’Content-Disposition: 表单数据; name=\’%@\’\r\n\r\n%@’, key, obj]; [formDataappendData:[thisFieldStringdataUsingEncoding:NSUTF8StringEncoding]]; [formData追加数据:lineData]; }]; //拼接上传的文件和信息[formDataappendData:boundary]; [formData追加数据:lineData]; NSString *thisFieldString=[NSString stringWithFormat: @’ Content- Disposition: 表单数据;名称=\’名称\’; filename=\’文件名\’\r\nContent-Type: mimetype’]; [formDataappendData:[thisFieldStringdataUsingEncoding:NSUTF8StringEncoding]]; [formData追加数据:lineData]; [formData追加数据:lineData]; [formData追加数据:data]; [formData追加数据:lineData]; [formData appendData: [[NSString stringWithFormat:@’–%@–\r\n’, Boundary] dataUsingEncoding:NSUTF8StringEncoding]]; NSString *filePath=[NSString stringWithFormat:@’%@/%ld’, self.segmentDocumentPath, self.currentIndex]; BOOL write=[formData writeToFile:filePathatomically:YES];返回写? filePath : nil;} 1.断点续传
整体文件上传断点续传,包括任务和分片两个维度。
如果uploadTasks队列现在包含三个上传任务,第一个正在上传,另外两个正在等待上传。如果退出应用,下次进入应用,仍然会从当前任务的上传进度开始,接下来的两个任务仍然在等待。状态。
任务的实现非常简单,只需退出应用程序并在特定时间持久化uploadTasks队列即可。分片断点上传以及如何保证分片能够正确上传到服务器是一个关键问题。
针对这个问题,我设计了双数组的方法。创建上传任务后,我根据特定的规则计算出单个大小的大小,并计算出源文件需要多少个分片。我提前创建了一个与count对应的数组。数组元素是分片。切片索引,命名为要上传的uploadSegments数组;后续上传任务会从uploadSegments数组中取出索引,对表单进行切片拼接进行上传;向服务器请求切片后,如果请求成功,则会将其从uploadSegments 中删除,并添加到successSegments 中。如果失败的话,你可能会遇到很多情况。如果由于网络波动等原因,会重试请求。如果文件格式等问题,将停止上传并提示错误;上传成功的分片索引将添加到successSegments 中。直到successSegments 的数量等于分片的数量,所有分片任务将被上传,并向服务器发送标识。即可完成整个任务上传;在退出应用程序之前,将保存两个数组successSegments 和uploadSegments。下次开始后续上传时,只需从uploadSegments中取出索引并执行分片逻辑即可。没错,我们的延续就是基于分片的。这个方案看似麻烦,但是对于保证上传成功率非常有效,可以解决上传恢复、上传失败等各种情况。
2.内存峰值
如果是高清视频文件,大小会更大,1G的文件也是存在的。因此,需要考虑大文件上传的问题。
需要注意的是,NSURLSession 有以下两种上传文件的方法。第二种方法会有内存问题,尤其是上传较大文件时,会出现较大的内存峰值。即使分块上传,内存峰值的问题仍然存在,可能会导致上传过程中崩溃。
– (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)来自File:的请求(NSURL *)fileURL;- (NSURLSessionUploadTask *)uploadTaskWithRequest:(NSURLRequest *)来自Data:的请求(NSData *)bodyData;无论你使用AFNetworking还是NSURLSession,这个问题都存在。方法解决方案是使用fromFile。
因此,我们采用fromFile方案,先对源文件进行切片,拼接表单,写入本地,然后将路径传入上传。这样就不会出现内存峰值崩溃的问题了。即使上传1G的视频,整体上传过程内存仍然会非常稳定。
3.异常处理
文件上传过程比较长,这期间可能会遇到很多情况。以下是一些常见问题以及如何处理这些问题。
如果内存空间不足,或者没有网络等,则需要暂停所有任务并保存相应的任务状态;对于未知的网络错误或其他异常网络问题,请重试3次。如果全部失败,则暂停任务;网络未授权或者飞行模式,提示用户并暂停任务。
原创文章,作者:小su,如若转载,请注明出处:https://www.sudun.com/ask/126121.html
用户评论
优雅的叶子
这个解决方案看起来很棒!我一直在烦恼如何提高多文件上传速度,你的方案结构清晰易懂,而且针对性很强。我一定试试看效果!
有5位网友表示赞同!
未来未必来
我对并行上传技术一直非常关注,这篇博文分析得非常到位,尤其对于不同类型的上传场景选择方案的分辨思路很有启发意义。希望未来能看到更多类似深入实际问题的方案讲解。
有12位网友表示赞同!
哭花了素颜
虽然内容很好,但我对具体的代码实现方式更感兴趣。能否分享一些实际的代码示例?这样更容易理解和应用你的方案!
有13位网友表示赞同!
熟悉看不清
我同意文中关于文件分块上传的说法,确实能有效提升效率。但对于网速较慢的情况,这种方案是否会加重服务器压力呢?还有没有其他方案可供参考?
有10位网友表示赞同!
柠栀
看到这篇博文让我眼前一亮,这个算法真是太巧妙了!以前总觉得多文件上传速度缓慢,如今有了这样的解决方案,简直太棒了!值得一试啊!
有14位网友表示赞同!
一别经年
我觉得文中提到的 HTTP 协商机制和任务调度策略都比较好理解,但也希望你能提供更多关于安全性方面的考虑,例如如何防范恶意攻击等。
有20位网友表示赞同!
命运不堪浮华
多文件上传一直是我开发过程中遇到的一个瓶颈问题,这款方案的设计非常实用。我正在尝试将其应用到我的项目中,期待能取得良好效果!
有17位网友表示赞同!
敬情
在实际的网络环境下,稳定性和抗干扰能力是否会受到影响?如何优化方案以提高其鲁棒性呢?希望能看到更多细节方面的阐述。
有20位网友表示赞同!
孤城暮雨
我曾经尝试过一些手动实现多文件并行上传的方法,但效果并不是很好。这款方案的设计思路很新颖,值得一学习!
有12位网友表示赞同!
全网暗恋者
针对不同的网络条件和文件类型,该方案是否有相应的调整机制?例如对于带宽有限的场景,如何更好地适应呢?希望在博文中能看到更多关于适用范围方面的探讨。
有15位网友表示赞同!
迷路的男人
这个方案主要针对的是什么类型的系统或应用场景呢?能否给一些具体的使用案例说明?这样更容易理解其价值和意义!
有18位网友表示赞同!
幸好是你
对于大型文件上传,该方案是否还有提升空间?例如可以考虑更加细化的文件分段策略或者更有效的负载均衡机制等。期待看到更多关于性能优化的思路。
有11位网友表示赞同!
沐晴つ
我觉得你提出的方案非常有创新性。尤其是在网络环境复杂的情况下,这种并行上传机制能有效提高上传速度和稳定性。
有16位网友表示赞同!
拉扯
虽然方案设计得很不错,但我仍然比较关注其在真实应用场景中的实际表现,尤其是对于实时交互的需求,是否存在延迟问题?是否会有相应的优化措施?
有12位网友表示赞同!
半世晨晓。
这个解决方案太棒了!对于我做网站开发来说,提高上传速度无疑能带来更好的用户体验。感谢你的分享!
有15位网友表示赞同!
我的黑色迷你裙
我认为该方案过于复杂,对于一些非专业开发人员来说可能不太易于理解和实施。能否提供更简化的配置方法或者使用教程?
有12位网友表示赞同!
病态的妖孽
多文件并行上传一直是我在开发过程中遇到的难题之一,这个方案能够有效解决这一问题,感谢分享!我会尽快尝试一下看看效果如何。
有5位网友表示赞同!
裸睡の鱼
我认为这篇博文写的不错,结构清晰易懂,但希望能更详细地介绍各个模块的实现细节,这样对于想深入学习的读者更有帮助。
有8位网友表示赞同!