go文件分片上传实现_go 分片上传_猿职场的博客-程序员宅基地

技术标签: golang  go技术  

文件分片上传

文件分片上传,解决大文件上传缓慢问题
实现思路:
1.使用js将文件按照指定的分片文件大小进行拆分
2.构建form表单数据
i:file 分片文件
ii: chunkindex 当前切片数据
iii: chunktotal 分片总数
iv: filesize 文件总大小
3.使用ajax逐渐上传文件
4.后台保存每一个上传文件
5.生成新文件,计算上传的每个分片文件总大小是否于前端传的文件总大小是否相等,相等则合并分片文件
6.删除分片文件

完整文件代码

https://github.com/yunziyuan/cntech-go/tree/main/chunkfile

开发http服务
// 文件服务
func fileServer() {
	http.HandleFunc("/chunkfile", chunkFile)
	// 监听8001端口
	err := http.ListenAndServe("0.0.0.0:8001", nil)
	if err != nil {
		log.Fatal("服务启动失败")
	}
}
处理文件
// 上传文件
func chunkFile(w http.ResponseWriter, r *http.Request) {
	// 设置跨域
	w.Header().Set("Access-Control-Allow-Origin", "*")             //允许访问所有域
	w.Header().Add("Access-Control-Allow-Headers", "Content-Type") //header的类型
	w.Header().Set("content-type", "application/json")             //返回数据格式是json
	// 合并
	res, err := mergeChunk(r)
	if err != nil {
		_, _ = w.Write([]byte(err.Error()))
	} else {
		// 保存上传节点
		_, _ = w.Write([]byte("上传成功:" + strconv.Itoa(res)))
	}
}
分片上传文件
// 分片上传
func chunkUpload(r *http.Request) (int, error) {
	// 分片序号
	chunkIndex := r.FormValue("chunkindex")
	// 获取上传文件
	upFile, fileHeader, err := r.FormFile("file")

	if err != nil {
		return 0, errors.New("上传文件错误")
	}

	// 新文件创建
	filePath := tmpFilePath + fileHeader.Filename + "_" + chunkIndex
	fileBool, err := createFile(filePath)
	if !fileBool {
		return 0, err
	}
	// 获取现在文件大小
	fi, _ := os.Stat(filePath)
	// 判断文件是否传输完成
	if fi.Size() == fileHeader.Size {
		return 0, errors.New("文件已存在, 不继续上传")
	}
	start := strconv.Itoa(int(fi.Size()))

	// 进行断点上传
	// 打开之前上传文件
	file, err := os.OpenFile(filePath, os.O_CREATE|os.O_WRONLY, os.ModePerm)
	defer file.Close()
	if err != nil {
		return 0, errors.New("打开之前上传文件不存在")
	}

	// 将数据写入文件
	count, _ := strconv.ParseInt(start, 10, 64)
	total, err := uploadFile(upFile, count, file, count)
	return total, err
}

// 上传文件
func uploadFile(upfile multipart.File, upSeek int64, file *os.File, fSeek int64) (int, error) {
	// 上传文件大小记录
	fileSzie := 0
	// 设置上传偏移量
	upfile.Seek(upSeek, 0)
	// 设置文件偏移量
	file.Seek(fSeek, 0)
	data := make([]byte, 1024, 1024)
	for {
		total, err := upfile.Read(data)
		if err == io.EOF {
			//fmt.Println("文件复制完毕")
			break
		}
		len, err := file.Write(data[:total])
		if err != nil {
			return 0, errors.New("文件上传失败")
		}
		// 记录上传长度
		fileSzie += len
	}
	return fileSzie, nil
}

合并文件
// 合并切片文件
func mergeFile(i int, fileName, filePath string) {
	// 打开之前上传文件
	file, err := os.OpenFile(filePath, os.O_CREATE|os.O_WRONLY, os.ModePerm)
	defer file.Close()
	if err != nil {
		log.Fatal("打开之前上传文件不存在")
		//return 0, errors.New("打开之前上传文件不存在")
	}
	// 分片大小获取
	fi, _ := os.Stat(tmpFilePath + fileName + "_0")
	chunkSize := fi.Size()
	// 设置文件写入偏移量
	file.Seek(chunkSize*int64(i), 0)
	iSize := strconv.Itoa(i)
	chunkFilePath := tmpFilePath + fileName + "_" + iSize
	fmt.Printf("分片路径:", chunkFilePath)
	chunkFileObj, err := os.Open(chunkFilePath)
	defer chunkFileObj.Close()
	if err != nil {
		log.Fatal("打开分片文件失败")
		//return 0, errors.New("打开分片文件失败")
	}

	// 上传总数
	totalLen := 0
	// 写入数据
	data := make([]byte, 1024, 1024)
	for {
		tal, err := chunkFileObj.Read(data)
		if err == io.EOF {
			// 删除文件 需要先关闭改文件
			chunkFileObj.Close()
			err := os.Remove(chunkFilePath)
			if err != nil {
				fmt.Println("临时记录文件删除失败", err)
			}
			fmt.Println("文件复制完毕")
			break
		}
		len, err := file.Write(data[:tal])
		if err != nil {
			log.Fatal("文件上传失败")
			//return 0, errors.New("文件上传失败")
		}
		totalLen += len
	}
	lock.Done()
	//return totalLen,nil
}
html主要代码
<script type="text/javascript">
// 每个文件切片大小定为10M
    var chunksize = 1024 * 1024 * 50;
    // 定义上传总切片数
    var chunktotal;
    // 设置上传成功数量记录
    successTotal = 0
    function upload() {
        var file = document.getElementById("file").files[0];
        var start = 0;
        var end;
        var index = 0;
        var filesize = file.size;
        var filename = file.name;

        // 计算总的切片数
        chunktotal = Math.ceil(filesize / chunksize);
        while(start < filesize) {
            end = start + chunksize;
            if(end > filesize) {
                end = filesize;
            }

            var chunk = file.slice(start,end);//切割文件
            var chunkindex = index;
            var formData = new FormData();
            // 新增切片文件
            formData.append("file", chunk, filename);
            // 切片索引
            formData.append("chunkindex", chunkindex);
            // 切片总数
            formData.append("chunktotal", chunktotal);
            // 文件总大小
            formData.append("filesize",filesize)
            // 使用ajax提交
            $.ajax({
                url: 'http://127.0.0.1:8001/chunkfile',
                type: 'POST',
                cache: false,
                data: formData,
                processData: false,
                contentType: false,
                success:function (res){
                    successTotal = successTotal + 1
                }
            }).done(function(res){
                console.log(res)
            }).fail(function(res) {
                console.log(res)
            });
            start = end;
            index++;
        }
    }
    </script>
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_35392791/article/details/113948356

智能推荐

Eclipse的常用快捷键_ecelipse 块复制-程序员宅基地

编辑相关快捷键 Eclipse的编辑功能非常强大,掌握了Eclipse快捷键功能,能够大大提高开发效率。Eclipse中有如下一些和编辑相关的快捷键。 1. 【ALT+/】 此快捷键为用户编辑的好帮手,能为用户提供内容的辅助,不要为记不全方法和属性名称犯愁,当记不全类、方法和属性的名字时,多体验一下【ALT+/】快捷键带来的好处吧。 _ecelipse 块复制

华为鸿蒙系统面对困难,缩水2亿!在关键时刻,华为突然改口,鸿蒙系统面临2大难点...-程序员宅基地

作为国内首个自研系统,华为的鸿蒙OS一直备受关注。然而,自2019年问世以来,鸿蒙系统可谓是波折不断,正式推送时间更是多次后延。华为突然改口根据此前华为官方的消息,鸿蒙系统将在4月份正式与用户见面。按照华为的时间表,如今正是鸿蒙发布的关键时刻,正当花粉们期待之时,华为官方却突然改口。在4月12日的华为分析师大会上,华为轮值董事长徐直军表示,2021年将有40多个主流品牌,超1亿台设备接入鸿蒙操作系..._鸿蒙开发的设计与实现的重难点

vue配置二级目录&vue-axios跨域办法&谷歌浏览器设置跨域-程序员宅基地

一。根据官方建议,dist打包的项目文件放在服务器根目录下,但是很多时候,我们并不能这样做,当涉及到二级目录设置多层深埋的时候,就需要在webpack配置文件里去设置一下了。在webpack.config.js(config—>index.js)文件里设置: build: { // Template for index.html index: path....

Javascript网课笔记(1)-程序员宅基地

先谈谈我对js的理解吧。js就是一门编程语言。哈哈没学啥也不知道。以下是我第一次上网课的学习笔记以及我自己的一些小总结。1.javascript的定义是WEB开发领域中的一种功能强大的编程语言,是一种可以嵌入到网页中的编程语言,用来控制浏览器行为。2.javascript的作用主要用于开发交互式的web页面,使网页的互动性更强,用户体验更好。3.javascript的特点js内嵌于..._javascript网课

跟锦数学2016年-程序员宅基地

(161231) 已知函数 $f(x)$ 的反函数是 $\varphi(y)$, 写出用 $f',f'',f'''$ 表示 $\varphi'$, $\varphi''$, $\varphi'''$ 的表达式.(161230) 设 $\sed{a_n}$ 递减趋于零, 试证: $$\bex \vsm{n}\f{a_n}{n}<\infty\lra a_n=O\sex{\...

Java计算组合数以及生成组合排列_java计算排列组合数的值-程序员宅基地

前言组合数计算公式法逐个相除法(错误)逐个相除法修正版素数幂乘法基本公式法平方差连乘法组合恒等法简单递归法杨辉三角法杨辉三角优化法二进制法组合数计算小结获取数组的组合排列二进制法基本迭代法从后向前迭代法(Matlab版本)从后向前优化法组合排列小结后记前言 最近学习了一下排列组合的相关知识,组合数..._java计算排列组合数的值

随便推点

Day10_04目标:字符缓冲输入流的使用_14、获取列表元素,15、将元素写入到缓冲流-程序员宅基地

目标:字符缓冲输入流的使用结构: 字节流 字符流 字节输入流 字节输出流 字符输入流 字符输出流 InputStream OutputStream Reader Writ..._14、获取列表元素,15、将元素写入到缓冲流

spring RequestMappingHandlerAdapter解析参数绑定到pojo过程_sprin6.0.0 requestmappinghandleradapter handleinte-程序员宅基地

springmvc 请求参数绑定到javabean源码解析_sprin6.0.0 requestmappinghandleradapter handleinternal

大话数据结构-程序员宅基地

大话数据结构数据结构:相互之间存在一种或多种特定关系的数据元素的集合。第一章 概述3.数据结构是一门研究非数值计算的程序设计问题中的操作对象,以及他们之间关系和操作等相关问题的学科。4.数据:描述客观事物的符号,是计算机中可操作的对象,可以被计算机识别,并输入给计算机处理的符号集合。(数值类型:整型、实型,非数值类型:声音、图像、视频,数据:即符号。1—可输入到计算机中,2—能够被计算机程序处理)数据元素:组成数据的、有一定意义的基本单位。数据项:一个数据元素可由多个数据项组成。(数据_大话数据结构

krpano全景之内置函数---asyncloop roundval 等(不定期更新)_krpano asyncloop-程序员宅基地

1.asyncloop首先是官方demo中的代码应用场景:(加载全景时的load...效果) set(loadingpercent_isloading, true); set(layer[loadingpercent_text].visible, true); asyncloop(loadingpercent_isloading, mul(pv, progress.pro_krpano asyncloop

YTU OJ C# 3437: 小豪要增重_和你熟的博客-程序员宅基地

YTU OJ C# 3437: 小豪要增重using System;class Program{ static void Main(String[] args) { var str1 = (Console.ReadLine()).Split(' '); var a = Convert.ToInt32(str1[0]); var b = Convert.ToInt32(str1[1]); var year = 0;

Java字符串-程序员宅基地

1.字符串的压缩public class Demon1 {public static String stringCompress(String str) { int count = 1; int low = 0; int high = 0; char c; StringBuilder stb = new StringBuilder(); while(low...