gin分片上传文件

为什么要使用分片上传

这个为什么已经是老篇常谈了,主要的原因无非就是文件比较大,一次性上传如果网络中断等情况客户端又得重新上传,而且没法补充上传。

切片上传流程

客户端:
有一个大文件,对这个文件进行切片,依据实际业务进行拆分,每一个文件片说白了就是一个[]byte
保证文件的一致性:
需要对每一个切片进行md5处理生成这个块的md5
提交内容:
参数:
文件id - 这个是当前文件上传的序列,保证整个上传中是对哪一个文件进行的上传操作
文件key - 当前切片的文件md5 服务端会进行二次校验保证当前文件切片一致
文件keys - 主文件所有切片的md5
文件切片内容 - 当前切片内容[]byte
文件名称 - 这个是后端用于确定的文件名称

服务端:
直接粗暴的使用json接收都可以,因为是接收的文件切片内容,file实际是一个[]byte
然后判断文件key是否与接收的file进行的md5处理一致,如果不一致则抛出异常即可
将当前切片数据写入临时文件
判断文件keys是否均已写入临时文件,如果没有直接返回保存成功即可
如果文件keys均已写入临时文件,那么就依次读文件keys进行读临时文件,然后写入到新文件即可

依据实际业务可以删除原临时文件或者也可以直接将临时文件上传到你的文件存储服务器,由它进行合并

源码: 纯go语言编写客户端及服务端
首先我们需要一个请求结构体

作者:Miajio
链接:https://www.jianshu.com/p/6b2ab6d0a082
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

package webimport ("crypto/md5""errors""fmt""net/http""os""path/filepath""github.com/gin-gonic/gin"
)type ChunkFileRequest struct {FileId   string   `json:"fileId"`   // client create uuidFileName string   `json:"fileName"` // file nameFileKeys []string `json:"fileKeys"` // file slice all key md5FileKey  string   `json:"fileKey"`  // file now key to md5 - if server read the slice to md5 eq key not eq then failFile     []byte   `json:"file"`     // now filectx *gin.Context // ctx
}func (cf *ChunkFileRequest) BindingForm(c *gin.Context) error {if err := c.ShouldBind(cf); err != nil {return err}cf.ctx = creturn cf.md5()
}func (cf *ChunkFileRequest) md5() error {fmt.Println(cf.FileKey)hash := fmt.Sprintf("%x", md5.Sum(cf.File))fmt.Println(hash)if hash != cf.FileKey {return errors.New("current file slice key error")}return nil
}func (cf *ChunkFileRequest) SaveUploadedFile(tempPath, path string) (string, error) {tempFolder := filepath.Join(tempPath, cf.FileId)_, err := os.Stat(tempFolder)if os.IsNotExist(err) {err := os.MkdirAll(tempFolder, os.ModePerm)if err != nil {return "", err}}out, err := os.Create(filepath.Join(tempFolder, cf.FileKey))if err != nil {return "", err}defer out.Close()if _, err := out.Write(cf.File); err != nil {return "", err}for _, fileKey := range cf.FileKeys {tempFile := filepath.Join(tempFolder, fileKey)if _, err := os.Stat(tempFile); err != nil {return "", nil}}base := filepath.Dir(path)if _, err := os.Stat(base); err != nil {if os.IsNotExist(err) {err := os.MkdirAll(base, os.ModePerm)if err != nil {return "", err}}}file, err := os.OpenFile(path, os.O_RDWR|os.O_APPEND|os.O_CREATE, 0664)if err != nil {return "", err}defer file.Close()for _, fileKey := range cf.FileKeys {tempFile := filepath.Join(tempFolder, fileKey)bt, err := os.ReadFile(tempFile)if err != nil {return "", err}file.Write(bt)}return tempFolder, nil
}// param: fileId
// param: fileName
// param: fileKeys the file slice all file key md5
// param: fileKey  now file slice key md5
// param: file     now slice file
func ChunkFile(c *gin.Context) {var cf ChunkFileRequestif err := cf.BindingForm(c); err != nil {c.JSON(http.StatusBadRequest, gin.H{"code": "400", "msg": "bad file param", "err": err.Error()})return}tempFolder, err := cf.SaveUploadedFile("./temp", "./uploads/"+cf.FileName)if err != nil {c.JSON(http.StatusServiceUnavailable, gin.H{"code": "503", "msg": "bad save upload file", "err": err.Error()})return}c.JSON(http.StatusOK, gin.H{"code": "200", "msg": "success"})if tempFolder != "" {defer func(tempFolder string) {os.RemoveAll(tempFolder)}(tempFolder)}
}

服务端及客户端测试代码:

func TestChunkFileUploadServer(t *testing.T) {ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)defer stop()w := gin.Default()w.POST("/chunkFile", web.ChunkFile)srv := &http.Server{Addr:    ":8080",Handler: w,}go func() {if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {log.Fatalf("listen: %s\n", err)}}()<-ctx.Done()stop()log.Println("shutting down gracefully, press Ctrl+C again to force")ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)defer cancel()if err := srv.Shutdown(ctx); err != nil {log.Fatal("Server forced to shutdown: ", err)}log.Println("Server exiting")
}func TestChunkFileUploadClient(t *testing.T) {// your client file pathfilePath := ""fileName := filepath.Base(filePath)fileInfo, err := os.Stat(filePath)if err != nil {log.Fatalf("file stat fail: %v\n", err)return}const chunkSize = 1 << (10 * 2) * 30num := math.Ceil(float64(fileInfo.Size()) / float64(chunkSize))fi, err := os.OpenFile(filePath, os.O_RDONLY, os.ModePerm)if err != nil {log.Fatalf("open file fail: %v\n", err)return}fileKeyMap := make(map[string][]byte, 0)fileKeys := make([]string, 0)for i := 1; i <= int(num); i++ {file := make([]byte, chunkSize)fi.Seek((int64(i)-1)*chunkSize, 0)if len(file) > int(fileInfo.Size()-(int64(i)-1)*chunkSize) {file = make([]byte, fileInfo.Size()-(int64(i)-1)*chunkSize)}fi.Read(file)key := fmt.Sprintf("%x", md5.Sum(file))fileKeyMap[key] = filefileKeys = append(fileKeys, key)}fileId := uuid.NewString()for _, key := range fileKeys {req := web.ChunkFileRequest{FileId:   fileId,FileName: fileName,FileKey:  key,FileKeys: fileKeys,File:     fileKeyMap[key],}body, _ := json.Marshal(req)res, err := http.Post("http://127.0.0.1:8080/chunkFile", "application/json", bytes.NewBuffer(body))if err != nil {log.Fatalf("http post fail: %v", err)return}defer res.Body.Close()msg, _ := io.ReadAll(res.Body)fmt.Println(string(msg))}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/70891.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Windows下搜索文件内容的关键字用什么命令

Windows下搜索文件内容的关键字用什么命令 findstr /s /n /i "keyword" file_path其中&#xff0c;/s 表示递归检索子文件夹&#xff0c;/n 表示显示搜索结果所在行号&#xff0c;/i 表示忽略大小写&#xff0c;“keyword” 是要搜索的关键字&#xff0c;file_path 是…

【LeetCode-中等题】17. 电话号码的字母组合

文章目录 题目方法一&#xff1a;递归回溯 题目 方法一&#xff1a;递归回溯 参考讲解&#xff1a;还得用回溯算法&#xff01;| LeetCode&#xff1a;17.电话号码的字母组合 首先可以画出树图&#xff1a; 先将数字对应的字符集合 加入到一个map集合 这里需要一个index来控…

MySQL为什么不推荐使用in

有的时候博客内容会有变动&#xff0c;首发博客是最新的&#xff0c;其他博客地址可能会未同步,认准https://blog.zysicyj.top 首发博客地址 系列文章地址 当使用IN语句时&#xff0c;MySQL可能会遇到以下问题&#xff1a; 索引问题&#xff1a;MySQL使用索引来加速查询&#x…

PPT怎么转换为PDF格式,收藏这两个在线工具。

PPT是一种常用的演示文稿格式&#xff0c;它可以包含丰富的动画效果和超链接&#xff0c;让你的内容更加生动和有趣。但是&#xff0c;如果你想将PPT分享给别人&#xff0c;或者在不同的设备上查看&#xff0c;你可能会遇到一些问题&#xff0c;比如&#xff1a; PPT文件太大&a…

使用Python实现二维应力云图

要画应力分布云图&#xff0c;可以使用Python中的科学计算和可视化库来实现 import numpy as np import matplotlib.pyplot as plt# 生成示例数据 x np.linspace(0, 10, 100) # X轴数据范围 y np.linspace(0, 5, 50) # Y轴数据范围 X, Y np.meshgrid(x, y) # 生成网…

Linux命令行

目录 CLI GUI 命令行界面 图形界面 命令行提示符 # $ ​编辑 命令一般由三个部分组成 历史命令&#xff0c;使用上下键&#xff0c;或者使用history&#xff0c;ctrlr搜索历史命令 通配符 *,? 切换用户 su 作业管理 &&#xff0c;jobs,bg,fg CLI GUI 命令行界面 …

接轨CCC国际标准,数字钥匙「出海」提速

作为汽车智能化时代的产物&#xff0c;数字钥匙正掀起全球化新趋势。 诚然&#xff0c;依托智能手机几近标配的NFC、BLE&#xff0c;数字钥匙可以实现远程操作、多人共享、人车互动等功能&#xff0c;不仅提升了整车的科技感和智能化水平&#xff0c;也提升了消费者的用车黏度…

内网穿透的应用-不再依赖iCloud!利用群晖生态,自己掌控本地SSD的云存储!

文章目录 前言本教程解决的问题是&#xff1a;按照本教程方法操作后&#xff0c;达到的效果是想使用群晖生态软件&#xff0c;就必须要在服务端安装群晖系统&#xff0c;具体如何安装群晖虚拟机请参考&#xff1a; 1. 安装并配置synology drive1.1 安装群辉drive套件1.2 在局域…

STM32 CAN快速配置(HAL库版本)

STM32 CAN快速配置&#xff08;HAL库版本&#xff09; 目录 STM32 CAN快速配置&#xff08;HAL库版本&#xff09;前言1 软件编程1.1 初始化1.1.1 引脚设置1.1.2 CAN参数设置1.1.3 CAN滤波器设置 1.2 CAN发送1.3 CAN接收 2 运行测试结束语 前言 控制器局域网总线&#xff08;CA…

Python基础String字符串定义与函数

字符串的函数 查看该数据类型都有哪些函数&#xff1a; 内置函数dir name "liming" print(dir(name))1.count() 统计某个字符出现的次数 name_info "liming" print(name_info.count("s")) // 结果是1 2.find() 查找某个元素在字符串中的索…

SpringBoot集成Microsoft office 365账号方案(InsCode AI 创作助手)

SpringBoot集成微软office 365账号需要进行以下步骤&#xff1a; 1. 注册Azure AD应用程序 要使用Microsoft Graph API访问Office 365数据&#xff0c;我们需要先注册一个Azure AD应用程序&#xff0c;以便获取相应的应用程序ID和机密。 2. 添加API权限 在Azure门户中为我们…

ElementUI浅尝辄止32:NavMenu 导航菜单

为网站提供导航功能的菜单。常用于网站平台顶部或侧边栏菜单导航。 1.如何使用&#xff1f;顶栏 /*导航菜单默认为垂直模式&#xff0c;通过mode属性可以使导航菜单变更为水平模式。另外&#xff0c;在菜单中通过submenu组件可以生成二级菜单。Menu 还提供了background-color、…

Linux查看端口使用情况

在 Linux 中&#xff0c;可以使用多种工具来查看端口使用情况&#xff0c;这些工具提供了正在监听或建立的网络连接的详细信息&#xff1a; 使用 netstat 命令: netstat&#xff08;网络统计&#xff09;是一个命令行工具&#xff0c;可以显示网络连接、路由表、接口统计等信息…

fastjson漏洞批量检测工具

JsonExp 简介 版本&#xff1a;1.3.5 1. 根据现有payload&#xff0c;检测目标是否存在fastjson或jackson漏洞&#xff08;工具仅用于检测漏洞&#xff09;2. 若存在漏洞&#xff0c;可根据对应payload进行后渗透利用3. 若出现新的漏洞时&#xff0c;可将最新的payload新增至…

在scroll-view中使用u-charts,滚动图表

1、问题 直接在scroll-view中使用可滚动的图表&#xff0c;图表是无法滚动的&#xff0c;图表的滚动事件和scroll-view的产生了冲突 2、解决方式 u-charts设置inScrollView为true&#xff0c;表示图表存在于scroll-view内u-charts设置disableScroll为true&#xff0c;拖动图表…

深入学习与探索:高级数据结构与复杂算法

文章目录 学习高级数据结构B树&#xff1a;数据库引擎的骨干线段树&#xff1a;高效的区间查询Trie树&#xff1a;高效的字符串检索 探索复杂算法领域图算法&#xff1a;解决复杂网络问题字符串匹配算法&#xff1a;处理文本搜索近似算法&#xff1a;在NP难题上取得近似解 结论…

通过HbaseClient来写Phoenix表实现

由于数据存储在Hbase上&#xff0c;并且上层使用了Phoenix来读写数据。并且由于数据的列字段不固定&#xff0c;并且可能由于Hbase表列和Phoenix的表列字段不一致&#xff0c;使用Phoenix写入的数据会导致写出报错的问题出现。所以这里直接使用HbaseClient写入到Hbase表中&…

kubesphere devops使用

一、创建项目 1 创建项目 企业管理员切换到相应企业空间(租户),创建项目&#xff0c;k8s集群会创建一个相同名字的namespace。如下图所示管理员创建一个ipaas-devops项目。 2.创建镜像拉取密钥信息 进入项目如ipaas-devops&#xff0c;选择配置->保密字典->创建&#xf…

“交叉轮”轮融资后,哪吒汽车能否脚踏“风火轮”续写逆袭故事?

2023年的新能源汽车江湖&#xff0c;烟波浩渺的水面下暗潮汹涌。 从特斯拉年初打响降价第一枪&#xff0c;降价潮至今未见尾声。9月刚至&#xff0c;小鹏汽车、零跑汽车又推出了调价政策。 这一背景下&#xff0c;车企内卷加剧是必然。年初&#xff0c;哪吒汽车联合创始人、C…

EOCR-AR电机保护器自动复位的启用条件说明

为适用不同的现场使用需求&#xff0c;施耐德韩国公司推出了带有自动复位功能的模拟型电动机保护器-EOCR-AR。EOCR-AR电机保护器具有过电流、缺相、堵转保护功能&#xff0c;还可根据实际需要设置自动复位时间。 EOCR-AR自动复位的设置方法 如上图&#xff0c;R-TIME旋钮是自动…