如何进行Go大文件上传?

您所在的位置:网站首页 golang上传大文件 如何进行Go大文件上传?

如何进行Go大文件上传?

#如何进行Go大文件上传?| 来源: 网络整理| 查看: 265

前端分割数据 这里可自行设定分片的大小 要不就是请求多分片小 要不就是分片多请求少

const chunkSize = 2 * 1024 * 1024; // 每个chunk的大小,设置为2兆 const blobSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice; const hashFile = (file) => { return new Promise((resolve, reject) => { const chunks = Math.ceil(file.size / chunkSize); let currentChunk = 0; const spark = new SparkMD5.ArrayBuffer(); const fileReader = new FileReader(); function loadNext() { const start = currentChunk * chunkSize; const end = start + chunkSize >= file.size ? file.size : start + chunkSize; fileReader.readAsArrayBuffer(blobSlice.call(file, start, end)); } fileReader.onload = e => { spark.append(e.target.result); // Append array buffer currentChunk++; if (currentChunk < chunks) { loadNext(); console.log(`第${currentChunk}分片解析完成,开始解析${currentChunk + 1}分片`); } else { console.log('finished loading'); const result = spark.end(); // 如果单纯的使用result 作为hash值的时候, 如果文件内容相同,而名称不同的时候 // 想保留两个文件无法保留。所以把文件名称加上。 const sparkMd5 = new SparkMD5(); sparkMd5.append(result); sparkMd5.append(file.name); const hexHash = sparkMd5.end(); resolve(hexHash); } }; fileReader.onerror = () => { console.warn('文件读取失败!'); }; loadNext(); }).catch(err => { console.log(err); }); } const fileDom = $('#file')[0]; // 获取到的files为一个File对象数组,如果允许多选的时候,文件为多个 const files = fileDom.files; const file = files[0]; if (!file) { alert('没有获取文件'); return; } const blockCount = Math.ceil(file.size / chunkSize); // 分片总数 const axiosPromiseArray = []; // axiosPromise数组 const hash = await hashFile(file); //文件 hash // 获取文件hash之后,如果需要做断点续传,可以根据hash值去后台进行校验。 // 看看是否已经上传过该文件,并且是否已经传送完成以及已经上传的切片。 for (let i = 0; i < blockCount; i++) { const start = i * chunkSize; const end = start + chunkSize >= file.size ? file.size : start + chunkSize; // 构建表单 const form = new FormData(); form.append('file', blobSlice.call(file, start, end)); form.append('name', file.name); form.append('total', blockCount); form.append('index', i); form.append('size', file.size); form.append('hash', hash); console.log(blockCount, blobSlice.call(file, start, end), i, start, end, file.size); // ajax提交 分片,此时 content-type 为 multipart/form-data const axiosOptions = { onUploadProgress: e => { // 处理上传的进度 // console.log(blockCount, i, e, file); }, }; // 加入到 Promise 数组中 axiosPromiseArray.push(axios.post('/uploadFile', form, axiosOptions)); } await axios.all(axiosPromiseArray).then((result) => { // 合并chunks const data = { size: file.size, name: file.name, total: blockCount, hash }; const form = new FormData(); form.append('size', file.size); form.append('name', file.name); form.append('total', blockCount); form.append('hash', hash); console.log(result); axios.post("/file/chunks", form).then(res => { console.log(res) }) }).catch((err) => { }); console.log("全部上传完毕"); })

后端接受后创建一个临时文件夹存储前端上传的文件 这里可以用redis存储上传的信息做断点续传

var dir, _ = os.Getwd() var uploadPath = path.Join(dir, "uploads") var uploadTempPath = path.Join(uploadPath, "temp") func sayhello(w http.ResponseWriter, r *http.Request) { r.ParseForm() //解析参数,默认是不会解析的 t, err := template.ParseFiles("static/index.html") if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } t.Execute(w, "张三") return } // 判断文件夹是否存在 func PathExists(path string) (bool, error) { _, err := os.Stat(path) if err == nil { return true, nil } if os.IsNotExist(err) { return false, nil } return false, err } func uploadFile(w http.ResponseWriter, r *http.Request) { file, _, err := r.FormFile("file") // total := r.PostFormValue("total") index := r.PostFormValue("index") // size, err := strconv.ParseInt(r.PostFormValue("size"), 10, 64) hash := r.PostFormValue("hash") // name := r.PostFormValue("name") // 获取uploads下所有的文件夹 nameList, err := ioutil.ReadDir(uploadPath) m := map[string]interface{}{ "code": 46900, "msg": "文件已上传", } result, _ := json.MarshalIndent(m, "", " ") // 循环判断hash是否在文件里如果有就返回上传已完成 for _, name := range nameList { tmpName := strings.Split(name.Name(), "_")[0] if tmpName == hash { fmt.Fprintf(w, string(result)) return } } chunksPath := path.Join(uploadTempPath, hash, "/") isPathExists, err := PathExists(chunksPath) if !isPathExists { err = os.MkdirAll(chunksPath, os.ModePerm) } destFile, err := os.OpenFile(path.Join(chunksPath+"/"+hash+"-"+index), syscall.O_CREAT|syscall.O_WRONLY, 0777) reader := bufio.NewReader(file) writer := bufio.NewWriter(destFile) buf := make([]byte, 1024*1024) // 1M buf for { n, err := reader.Read(buf) if err == io.EOF { writer.Flush() break } else if err != nil { return } else { writer.Write(buf[:n]) } } defer file.Close() defer destFile.Close() if err != nil { log.Fatal("%v", err) } } // 合并文件 func chunks(w http.ResponseWriter, r *http.Request) { // total, _ := strconv.Atoi(r.PostFormValue("total")) // index := r.PostFormValue("index") size, _ := strconv.ParseInt(r.PostFormValue("size"), 10, 64) hash := r.PostFormValue("hash") name := r.PostFormValue("name") toSize, _ := DirSize(path.Join(uploadTempPath, hash, "/")) if size != toSize { fmt.Fprintf(w, "文件上传错误") } chunksPath := path.Join(uploadTempPath, hash, "/") files, _ := ioutil.ReadDir(chunksPath) fs, _ := os.OpenFile(path.Join(uploadPath, hash+"_"+name), os.O_CREATE|os.O_RDWR|os.O_APPEND, os.ModeAppend|os.ModePerm) var wg sync.WaitGroup wg.Add(len(files)) for i, f := range files { go func(f os.FileInfo) { // 这里一定要注意按顺序读取不然文件就会损坏 name := strings.Split(f.Name(), "-")[0] + "-" + strconv.Itoa(i) fileName := path.Join(chunksPath, "/"+name) data, _ := ioutil.ReadFile(fileName) fs.Write(data) os.RemoveAll(path.Join(chunksPath, "/")) defer wg.Done() }(f) } wg.Wait() m := map[string]interface{}{ "code": 20000, "msg": "上传成功", } result, _ := json.MarshalIndent(m, "", " ") fmt.Fprintf(w, string(result)) defer fs.Close() } // 获取整体文件夹大小 func DirSize(path string) (int64, error) { var size int64 err := filepath.Walk(path, func(_ string, info os.FileInfo, err error) error { if !info.IsDir() { size += info.Size() } return err }) return size, err } func main() { http.HandleFunc("/", sayhello) // set router http.HandleFunc("/uploadFile", uploadFile) http.HandleFunc("/file/chunks", chunks) err := http.ListenAndServe(":8080", nil) // set listen port if err != nil { log.Fatal("Error while starting GO http server on port - 8080 : ", err) //log error and exit in case of error at server boot up } }

第一次写可能不是最优解 写的不好请大家多多包涵



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3