先获取文件大小,然后将文件切片传输给前端,前端再按顺序组合所有切片。
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
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)
}