目的: 让 http body 中全是文件内容, 不包含其他东西。
因为大文件直接上传到服务器上会占用大量内存, 为了节省内存采用了流式读取 http body, 然后写入文件
但是 body 中可能有其他 post 参数, 如何边读边解析出文件内容和这些参数呢?
已知可以让前端 body 采用 binary 格式, 但是受限于前端组件不支持这种格式, 推进很困难。
1
shoaly 2021-01-19 19:00:56 +08:00
拆分, 把单纯的文件上传剥离出来, 上传好来 返回一个 media_id , 然后将 media_id 和 其他 post 参数 再第二次传过来
|
2
MarkMelon OP @shoaly 首先感谢。 我们现在已经让前端把大文件拆成小文件上传了。
现在要解决的是, 流式读取 body 并保存到文件中。 比如每次从网卡里读 4k 这样的。 但是 body 里 form/data 会有分隔符还有其他参数。 如何在流式读取过程中把文件和这些参数区分开 |
4
Jirajine 2021-01-19 19:12:17 +08:00 via Android
把文件和其他参数混到一个 body 中不是合适的做法,但即使这么作了也不影响流式解析。以 JSON 为例,先搞个 json.Decoder 把那些参数读出来,然后剩下的在写到磁盘。
|
7
jindeq 2021-01-19 19:58:20 +08:00 via Android
form-data 形式的表单是支持的,我现在也有个接口在用。gin 可以取 body 里的参数和文件
|
8
Jirajine 2021-01-19 20:07:51 +08:00 via Android
@MarkMelon 要是文件数据包含到参数里面那就稍微麻烦点,你得手动解析。
具体就是流式地读,读到参数部分就解析出来,读到文件部分就写到磁盘上,再读到参数部分再解析出来,以此类推。 |
9
matrix67 2021-01-19 20:14:10 +08:00
上传大文件还挺麻烦的,这个 1.1k ,go 下面 star 最多的 httpserver,实现的就有问题,大文件上传会挂掉。
https://github.com/codeskyblue/gohttpserver/issues/98 |
10
loading 2021-01-19 20:22:24 +08:00 via Android
还没有分片上传然后合并的中间件吗?坐等一个最优解。
|
11
zu1k 2021-01-19 21:20:43 +08:00
其实完全不需要担心,net/http 在 readForm 的时候如果文件超过指定的最大内存占用,会自动写入临时文件,所以根本不会占用太多内存
https://github.com/golang/go/blob/master/src/mime/multipart/formdata.go#L91 |
12
coosir 2021-01-19 21:29:07 +08:00
https://github.com/fabu-dev/fabu.dev
这个项目里面上传文件用到了分片与合,可参考 可以追下 /api/application/service/app.go 中的 Upload 方法 |
14
MarkMelon OP partReader, err := ctx.Request.MultipartReader()
if err != nil { this.logger.WithError(err).Error("UploadChunk invalid http body.") result.ErrorCode = this.getErrorCodes().ParamError return } for { part, err := partReader.NextPart() if part == nil && err == io.EOF { break } if err != nil && err != io.EOF { this.logger.Error("read body failed, err: ", err) if err = os.Remove(chunkPath); err != nil { this.logger.Error("read body failed remove chunk file failed, err: ", err) } result.ErrorCode = this.getErrorCodes().ChunkReadError return } if part.FileName() != "" { // 从 http 流中循环读取并写到文件中 _, err := io.CopyBuffer(file, part, buf) // _, err := io.CopyBuffer(file, ctx.Request.Body, buf) // 直接解析 http body, 读取 binary 数据,body 中只包含文件内容 if err != nil { this.logger.Error("write data from part to file failed, err: ", err) if err = os.Remove(chunkPath); err != nil { this.logger.Error("write data from part to file failed and remove chunk file failed, err: ", err) } result.ErrorCode = this.getErrorCodes().ChunkWriteError return } } } |