大文件上传的三个难点:
性能问题:单线程上传大文件速度慢,尤其在网络不稳定时容易中断。
断点续传:需要支持从上次中断的位置继续上传,而非重新开始。
顺序组装:分片上传后,服务器端必须按正确顺序合并分片,否则文件会损坏。
1.文件上传oss
1.文件上传到oss服务器
在当前工程里,上传文件到 OSS 服务器的流程依据文件类型(图片、视频、普通文件)和是否为敏感文件有所不同,下面分别总结不同场景下的上传流程。
普通文件上传(非敏感文件)
以 UploadFileOSS 函数为例,普通文件上传流程如下:
获取上传文件:从 HTTP 请求表单里获取名为 file 的文件。
检查文件大小:验证文件大小是否超出 maxFileSize(50MB),若超出则返回错误。
验证文件类型:获取文件的 MIME 类型,检查其是否在 allowedFileTypes 列表中,若不在则返回错误。
生成唯一文件名:依据当前时间戳生成唯一文件名。
获取 OSS Bucket 对象:从 model 包获取 OSS 客户端和存储桶名称,得到对应的 Bucket 对象。
打开文件:打开上传的文件。
上传文件到 OSS:调用 bucket.PutObject 方法将文件上传到 OSS。
返回结果:上传成功后,拼接文件外部访问 URL 并返回给客户端。
敏感文件上传
同样在 UploadFileOSS 函数中,敏感文件上传流程如下:
获取上传文件:从 HTTP 请求表单获取名为 file 的文件。
检查文件大小:验证文件大小是否超出 maxFileSize(50MB),若超出则返回错误。
验证文件类型:获取文件的 MIME 类型,检查其是否在 allowedFileTypes 列表中,若不在则返回错误。
读取文件内容:打开上传文件并读取其内容。
获取加密密钥:从数据库获取加密密钥。
加密文件内容:使用获取的密钥对文件内容进行加密。
生成保存路径:生成唯一文件名并拼接保存路径。
保存加密文件:将加密后的文件保存到本地。
记录文件信息:将文件记录入库,包含 is_sensitive = true 和存储路径信息。
记录访问日志:记录文件访问日志。
返回结果:返回上传成功信息,包含文件 ID、文件路径和密钥摘要信息。
图片上传
以 UploadImageOSS 函数为例,图片上传流程如下:
获取上传图片:从 HTTP 请求表单获取名为 image 的文件。
检查文件大小:验证文件大小是否超出 maxImageSize(5MB),若超出则返回错误。
验证文件类型:获取文件的 MIME 类型,检查其是否在 allowedImageTypes 列表中,若不在则返回错误。
生成唯一文件名:依据当前时间戳生成唯一文件名。
打开文件并解码图像:打开上传文件并解码图像。
获取 OSS Bucket 对象:从 model 包获取 OSS 客户端和存储桶名称,得到对应的 Bucket 对象。
处理压缩图片:对解码后的图像进行压缩处理。
上传文件到 OSS:将压缩后的图片上传到 OSS。
返回结果:上传成功后,拼接压缩图、缩略图和水印图的外部访问 URL 并返回给客户端。
视频上传
以 UploadToOSS 函数为例,视频上传流程如下:
打开本地文件:使用 os.Open 函数打开本地视频文件。
获取 OSS Bucket 对象:从 model 包获取 OSS 客户端和存储桶名称,得到对应的 Bucket 对象。
开启分块上传:调用 InitiateMultipartUpload 方法开启分块上传任务。
分片上传:循环读取本地文件,将其分成多个分片,依次上传每个分片。
完成分片上传:调用 CompleteMultipartUpload 方法通知 OSS 服务将所有上传的分片合并成一个完整的文件。
记录日志:上传成功后记录上传成功的日志信息。
2.实现有顺序的协程

1.并发执行+有序结果收集
这种的适合大文件上传与下载,用协程并行执行,执行完一个协程后,将这个协程对应的id写入切片,信号写入值,然后输出的时候直接遍历这个切片,就一定是0,1,2,3这种的输出结果,得到结果的有序。这个是当所有协程执行完后才开始输出,只需要遍历切片就可以了。
results := make([]string, 3) // 预分配固定长度的切片
// 协程直接写入切片的固定索引位置
results[id] = result
// 主协程等待所有协程完成后按序遍历切片
wg.Wait()
for _, res := range results {
fmt.Println(res) // 此时所有结果已就绪
}必须等待所有任务完成(
wg.Wait())才能开始处理结果。类似 “大文件下载”:必须等所有分片下载完才能拼接成完整文件。
2.并发执行+按序消费
这种适合于在线视频(流式场景),直播弹幕,音乐播放,并发获取数据片段(提前准备 “未来需要的数据”),但使用时必须 “按原始顺序依次处理”,数据用完即弃,不需要长期保存。
缓冲区是由链表加锁实现的,带缓冲的通道不行,因为他是必须顺序执行的,导致没法消费,所以要从带缓冲的通道中获取到信号后存储到链表中(链表可以随意插入和删除,这个时候加锁保证不会出现问题)。

评论