前端vue后端go实现大文件分片下载

先获取文件大小,然后将文件切片传输给前端,前端再按顺序组合所有切片。

vue

    /** 下载数据 */
    handleDownloadInstance(id, instanceName) {
      if (this.downloadProgressisVisible) {
        this.$showError('已经有文件在下载!')
        return
      }
      this.$showInfo('开始下载!')
      getFileSize(id).then((response) => {
        this.downloadProgressisVisible = true
        const fileSize = response.size
        this.fileSize = fileSize
        this.downloadPercentage = 0
        this.currentSize = 0
        console.log(response)

        // 计算分片数和每片大小
        const chunkSize = 10 * 1024 * 1024 // 10 MB
        const totalChunks = Math.ceil(fileSize / chunkSize)

        // 保存所有分片的数据
        const allChunks = new Array(totalChunks)
        const self = this
        let num = 0
        // 下载所有分片
        function downloadChunks() {
          for (let i = 0; i < totalChunks; i++) {
            const data = {
              start: i * chunkSize,
              end: (i + 1) * chunkSize - 1
            }
            downloadInstanceChunk(id, data).then(response => {
              return response
            })
              .then(chunkBlob => {
                // 保存分片数据
                allChunks[i] = chunkBlob
                num += 1
                // console.log('下载完成', i)
                // 检查是否所有分片都下载完成
                if (num === totalChunks) {
                  mergeChunks()
                  self.$showSuccess('下载成功!')
                  self.downloadProgressisVisible = false
                }
                self.downloadPercentage = Math.floor((num) / totalChunks * 100)
                self.currentSize = num * chunkSize
              })
              .catch(error => {
                console.error('Error:', error)
                self.downloadProgressisVisible = false
                self.$showError('下载文件失败:' + error.data.message)
              })
          }
        }

        // 合并所有分片
        function mergeChunks() {
          // 创建一个 Blob 对象,包含所有分片数据
          const mergedBlob = new Blob(allChunks, { type: 'application/zip' })

          // 创建下载链接并模拟点击
          const downloadLink = document.createElement('a')
          downloadLink.href = URL.createObjectURL(mergedBlob)
          const fileName = `${instanceName}.zip`
          downloadLink.download = fileName
          downloadLink.click()
        }

        // 调用下载分片函数
        downloadChunks()
      }).catch(err => {
        console.log(err)
        this.$showError('下载文件失败:' + err.data.message)
        this.downloadProgressisVisible = false
      })
    },

go

func (handler *Handler) getFileSize(w http.ResponseWriter, r *http.Request) *httperror.HandlerError {
	instanceID, err := request.RetrieveNumericRouteVariableValue(r, "id")
	if err != nil {
		return &httperror.HandlerError{StatusCode: http.StatusBadRequest, Message: "Invalid instance identifier route variable", Err: err}
	}

	instance, err := handler.DataStore.Instance().Instance(uint(instanceID))
	if err == bolterrors.ErrObjectNotFound {
		return &httperror.HandlerError{StatusCode: http.StatusNotFound, Message: "Unable to find a instance with the specified identifier inside the database", Err: err}
	} else if err != nil {
		return &httperror.HandlerError{StatusCode: http.StatusInternalServerError, Message: "Unable to find a instance with the specified identifier inside the database", Err: err}
	}

	// timeStr := instance.ReportTime.Format("2006-01-02 15:04:05")
	// timeStr = strings.Replace(timeStr, ":", "-", -1)
	// timeStr = strings.Replace(timeStr, " ", "-", -1)

	dirPath := instance.TempPath
	zipPath := instance.InstanceName + ".zip"

	zipPath = "/home/1.zip"
	dirPath = "/home/
	if !utils.DirExists(dirPath) {
		return &httperror.HandlerError{StatusCode: http.StatusNotFound, Message: "文件不存在", Err: fmt.Errorf("文件不存在")}
	}
	_, err = os.Stat(zipPath)
	if err != nil {
		err = utils.ZipDir(dirPath, zipPath, []string{".pcap"})
		if err != nil {
			return &httperror.HandlerError{StatusCode: http.StatusInternalServerError, Message: "压缩文件失败", Err: fmt.Errorf("压缩文件失败")}
		}
	}
	fileInfo, err := os.Stat(zipPath)
	if err != nil {
		return &httperror.HandlerError{StatusCode: http.StatusInternalServerError, Message: "os.Stat 失败", Err: err}
	}
	return response.JSON(w, map[string]int64{
		"size": fileInfo.Size(),
	})
}

func (handler *Handler) downloadInstanceChunk(w http.ResponseWriter, r *http.Request) *httperror.HandlerError {
	instanceID, err := request.RetrieveNumericRouteVariableValue(r, "id")
	if err != nil {
		return &httperror.HandlerError{StatusCode: http.StatusBadRequest, Message: "Invalid instance identifier route variable", Err: err}
	}

	start, err := request.RetrieveNumericRouteVariableValue(r, "start")
	if err != nil {
		return &httperror.HandlerError{StatusCode: http.StatusBadRequest, Message: "Invalid instance identifier route variable", Err: err}
	}
	end, err := request.RetrieveNumericRouteVariableValue(r, "end")
	if err != nil {
		return &httperror.HandlerError{StatusCode: http.StatusBadRequest, Message: "Invalid instance identifier route variable", Err: err}
	}

	instance, err := handler.DataStore.Instance().Instance(uint(instanceID))
	if err == bolterrors.ErrObjectNotFound {
		return &httperror.HandlerError{StatusCode: http.StatusNotFound, Message: "Unable to find a instance with the specified identifier inside the database", Err: err}
	} else if err != nil {
		return &httperror.HandlerError{StatusCode: http.StatusInternalServerError, Message: "Unable to find a instance with the specified identifier inside the database", Err: err}
	}

	// timeStr := instance.ReportTime.Format("2006-01-02 15:04:05")
	// timeStr = strings.Replace(timeStr, ":", "-", -1)
	// timeStr = strings.Replace(timeStr, " ", "-", -1)

	dirPath := instance.TempPath
	zipPath := instance.InstanceName + ".zip"

	zipPath = "/home/1.zip"
	dirPath = "/home/test"
	if !utils.DirExists(dirPath) {
		return &httperror.HandlerError{StatusCode: http.StatusNotFound, Message: "文件不存在", Err: fmt.Errorf("文件不存在")}
	}

	// err = utils.ZipDir(dirPath, zipPath, []string{".pcap"})
	if err != nil {
		return &httperror.HandlerError{StatusCode: http.StatusInternalServerError, Message: "压缩文件失败", Err: fmt.Errorf("压缩文件失败")}
	}
	fileInfo, err := os.Stat(zipPath)
	if err != nil {
		return &httperror.HandlerError{StatusCode: http.StatusInternalServerError, Message: "os.Stat 失败", Err: err}
	}
	// 计算最后一片的范围
	if end == 0 || int64(end) > fileInfo.Size() {
		end = int(fileInfo.Size()) - 1
	}
	zipFile, err := os.Open(zipPath)
	if err != nil {
		return &httperror.HandlerError{StatusCode: http.StatusInternalServerError, Message: "文件无法打开", Err: fmt.Errorf("文件无法打开")}
	}
	defer zipFile.Close()

	w.Header().Set("Content-Type", "application/zip")
	// w.Header().Set("Content-Type", "application/octet-stream")
	// w.Header().Set("Content-Length", strconv.FormatInt(fileInfo.Size(), 10))
	// 设置响应头部,指定传输的范围
	w.Header().Set("Content-Range", "bytes "+strconv.FormatInt(int64(start), 10)+"-"+strconv.FormatInt(int64(end), 10))
	w.WriteHeader(http.StatusPartialContent)

	// 将部分内容传输到客户端
	_, err = zipFile.Seek(int64(start), 0)
	if err != nil {
		return &httperror.HandlerError{StatusCode: http.StatusInternalServerError, Message: "Unable to seek to the specified position", Err: err}
	}

	io.CopyN(w, zipFile, int64(end)-int64(start)+1)

	return response.Empty(w)
}

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
分片上传指将大文件拆分成若干小块进行上传,可以有效地提高上传的效率和稳定性,降低上传失败的概率。在前端Vue框架中,可以使用插件或组件实现分片上传功能,例如Element-UI或Vue-Upload-Component。而在后端Java中,可以使用SpringBoot或SpringMVC进行文件上传处理,同时结合使用阿里云、七牛云等云存储服务提高上传速度和可靠性。 具体实现流程如下: 1. 前端分片上传:前端先通过文件选择器选择要上传的文件,然后使用分片算法将大文件拆分成小块进行上传,每个小块的大小可以自行设置。前端也需要处理上传过程中的中断、暂停、恢复等问题,以确保文件上传的完整性和稳定性。 2. 后端接收上传请求:后端使用SpringBoot或SpringMVC接收前端的上传请求,包括文件信息和文件分块信息。后端可以对请求做一些校验,例如文件是否存在,上传的文件大小是否超过限制等等。 3. 后端分块合并:后端通过计算分块的MD5值,将分块合并成完整的文件。合并后的文件可以临时存储在本地磁盘,也可以直接上传到阿里云、七牛云等云服务提供商的存储空间中。 4. 后端上传完毕事件:文件上传完毕后,后端会返回相应的上传结果给前端前端可以进行相关处理,例如提示上传成功或上传失败等。 总之,前端Vue后端Java可以相互配合,实现高效稳定的文件分片上传功能。需要注意的是,在实际项目中,还需要考虑一些安全性和可靠性方面的问题,例如上传速度和可靠性、防止盗链和重复上传等等。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值