golang实现文件上传(高并发+分块+断点续传+加密)

运行视频

// todo 根据前端传递文件加密
func (s *FileProcess) FileProcessEncryptionByFront(file multipart.File, h *multipart.FileHeader) interface{} { //根据字节直接处理文件  这个是前端传递的二进制流
	s.FileProcessInit()               //文件初始化 设置原来文件的缓冲区和加密缓冲区的字节大小
	s.SourceFile.SetSize(int(h.Size)) //设置原文件的总字节
	s.SourceFile.SetName(h.Filename)  //设置原文件的名称
	s.EncryptFile.SetName(h.Filename) //设置加密文件后的名称
	filePath := s.EncryptFile.Name
	save, _ := os.OpenFile(s.EncryptFile.Name, os.O_CREATE|os.O_RDWR, os.ModePerm) //打开加密文件
	fileBool, err := isExists(filePath)
	if fileBool && err == nil {
		fmt.Println("文件已经存在")
	} else {
		newfile, err := os.Create(filePath) //创建当前文件
		defer newfile.Close()
		if err != nil {
			fmt.Println("创建文件失败")
		}
	}
	s.SetNumberDncrypted(s.SourceFile) //设置原文件的携程数量 因为是根据原文件加密
	TODO block := head.Size / 5 //把数据分成五片  一般不建议这种方式  因为文件太大的话分的单位也会太大  使用这种方式 可以把一片大小理解成一个缓冲区
	var wg sync.WaitGroup

	for i := 0; i < s.NumberCoroutine; i++ {
		wg.Add(1) //确保所有的文件合并
	}

	for i := 0; i < s.NumberCoroutine; i++ {
		//	if i != 3 && i != 6 { //模拟网络中断导致某个数据库块没有上传
		go func(i int) {
			defer wg.Done()
			buf := make([]byte, s.SourceFile.BockByteNum) // 定义一个缓冲区,用于读取文件块数据
			offset := i * s.SourceFile.BockByteNum
			save_offset := i * s.EncryptFile.BockByteNum       //todo 这部要注意 当前加密保存后文件的一个偏移量
			bufSave := make([]byte, s.EncryptFile.BockByteNum) //加密保存文件的缓冲区不一样
			_, err := save.ReadAt(bufSave, int64(save_offset)) //判断当前的文件偏移量是否有数据
			if !(err != nil && err != io.EOF) {                //如果没有数据
				n1, err1 := file.ReadAt(buf, int64(offset)) //读取原来上传文件的完整数据
				if err1 != nil && err1 != io.EOF {
					panic(err1.Error())
				}
				byte1, _ := tool.Encrypt(buf[:n1])               //字节加密
				_, err = save.WriteAt(byte1, int64(save_offset)) // 从偏移量处写入文件块数据
				//
				//if i == 0 {
				//	for i := 0; i < 10; i++ {
				//		fmt.Println("上传前100个原文", buf[i])
				//	}
				//}
				//if i == numThread-1 {
				//	//fmt.Println("最终转换字节数量为", len(getByte), "")
				//	for i := len(buf[:n1]) - 10; i < len(buf[:n1]); i++ {
				//		fmt.Println("上传原文最后十个字节数量为", buf[i])
				//	}

			}
		}(i)
	}
	wg.Wait()
	//设置加密文件的缓冲区
	return nil
}
  • 8
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
基于go的大文件切片上传、断点续传、秒传.zip 1、如何唯一标示一个文件文件的信息后端会存储在mysql数据库表中。 在上传之前,前端通过 spark-md5.js 计算文件的md5值以此去唯一的标示一个文件。 spark-md5.js 地址:satazor/js-spark-md5 README.md中有spark-md5.js的使用demo,可以去看看。 2、断点续传是如何实现的? 断点续传可以实现这样的功能,比如RD上传200M的文件,当用户上传完199M时,断网了,有了断点续传的功能,我们允许RD再次上传时,能从第199M的位置重新上传。 实现原理: 实现断点续传的前提是,大文件切片上传。然后前端得问后端哪些chunk曾经上传过,让前端跳过这些上传过的chunk就好了。 前端的上传器(uploader.js)在上传时会先发送一个GET请求,这个请求不会携带任何chunk数据,作用就是向后端询问哪些chunk曾经上传过。 后端会将这些数据保存在mysql数据库表中。比如按这种格式:1:2:3:5表示,曾经上传过的分片有1,2,3,5。第四片没有被上传,前端会跳过1,2,3,5。 仅仅会将第四个chunk发送给后端。 3、秒传是如何实现的? 秒传实现的功能是:当RD重复上传一份相同的文件时,除了第一次上传会正常发送上传请求后,其他的上传都会跳过真正的上传,直接显示秒成功。 实现方式: 后端存储着当前文件的相关信息。为了实现秒传,我们需要搞一个字段(isUploaded)表示当前md5对应的文件是否曾经上传过。 后端在处理 前端的上传器(uploader.js)发送的第一个GET请求时,会将这个字段发送给前端,比如 isUploaded = true。前端看到这个信息后,直接跳过上传,显示上传成功。 4、上传暂停是如何实现的? 上传的暂停:并不是去暂停一个已经发送出去的正在进行数据传输的http请求~ 而是暂停发送起发送下一个http请求。 就我们的项目而言,因为我们的文件本来就是先切片,对于我们来说,暂停文件的上传,本质上就是暂停发送下一个chunk。 5、前端上传并发数是多少? 前端的uploader.js中默认会三条线程启动并发上传,前端会在同一时刻并发 发送3个chunk,后端就会相应的为每个请求开启三个协程处理上传的过来的chunk。 在我们的项目中,会将前端并发数调整成了1。原因如下: 因为考虑到了断点续传实现后端需要记录下曾经上传过哪些切片。(这个记录在mysql的数据库表中,以 ”1:2:3:4:5“ )这种格式记录。 Mysql5.7默认的存储引擎是innoDB,默认的隔离级别是RR。如果我们将前端的并发数调大,就会出现下面的异常情况: 1. goroutine1 获取开启事物,读取当前上传到记录是 1:2 (未提交事物) 2. goroutine1 在现有的记录上加上自己处理的分片3,并和现有的1:2拼接在一起成1:2:3 (未提交事物) 3. goroutine2 获取开启事物,(因为RR,所以它读不到1:2:3)读取当前上传到记录是 1:2 (未提交事物) 4. goroutine1 提交事物,将1:2:3写回到mysql 5. goroutine2 在现有的记录上加上自己处理的分片4,并和现有的1:2拼接在一起成1:2:4 (提交事物) 可以看到,如果前端并发上传,后端就会出现分片丢失的问题。 故前端将并发数置为1。
实现高并发分块接收文件服务器,可以使用 Golang 的标准库 `net/http` 实现 HTTP 服务端,同时使用 `multipart/form-data` 实现文件上传功能。 以下是一个简单的示例代码: ```go package main import ( "fmt" "io/ioutil" "net/http" "os" ) func main() { http.HandleFunc("/upload", uploadHandler) http.ListenAndServe(":8080", nil) } func uploadHandler(w http.ResponseWriter, r *http.Request) { // 获取上传的文件 file, handler, err := r.FormFile("file") if err != nil { fmt.Println("Error Retrieving the File") fmt.Println(err) return } defer file.Close() // 读取上传的文件内容 fileBytes, err := ioutil.ReadAll(file) if err != nil { fmt.Println(err) } // 将文件内容写入本地文件 err = ioutil.WriteFile(handler.Filename, fileBytes, 0644) if err != nil { fmt.Println(err) } fmt.Fprintf(w, "File Uploaded Successfully") } ``` 以上代码实现了一个简单的文件上传功能,但是并没有实现分块上传和高并发处理。 要实现分块上传,可以使用 `multipart/form-data` 的 `Content-Range` 头部,将上传的文件切分成多个块,分别上传,最后合并成一个完整的文件。 要实现高并发处理,可以使用 Golang 的协程(goroutine)和通道(channel)机制。可以在接收到上传请求后,启动一个新的协程进行处理,将上传的文件块放入通道中,再启动多个协程进行文件块的处理和合并。 由于实现高并发分块接收文件服务器需要考虑的因素比较多,建议您参考一些已有的成熟的开源项目,例如:https://github.com/minio/minio 或 https://github.com/tus/tusd 等。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

超维Ai编程

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值