下面是对实现"React+EggJs实现断点续传的示例代码"的完整攻略。
简介
断点续传是指在上传或下载大文件时,当网络连接中断或者出现其他问题时,可以保证文件的上传或下载不会从头开始,而是从中断的位置继续进行。
本文将通过React + Egg JS框架实现断点续传功能,具体实现过程会在下面的代码示例中讲解。
技术栈
- 前端:React
- 后端:Egg JS(基于 NodeJS)
实现过程
前端代码实现
前端需要实现上传大文件以及断点续传功能。断点续传实现的关键之一就是要获取文件上传时断点的位置,这里我们使用axios库上传文件并在上传时记录文件上传位置。
文件上传时需要按照如下步骤进行:
-
将要上传的文件切成若干份,每份的大小为固定值。
-
上传第一份之前,请求后端接口获取当前上传的位置Pos。
-
从当前位置开始,上传文件的每份内容。并在上传过程中,记录文件上传的位置。
-
当文件上传完成时,通知后端文件上传完成。
下面是上传代码的示例。
import fs from 'fs'
import axios from 'axios'
const sliceSize = 1024 * 1024 // 切片的大小为1MB
const UploadUrl = "http://localhost:7001/api/file/upload"
function uploadFile(file) {
const fileSize = file.size
let start = 0
let end = sliceSize
let pos = 0 // 记录上传进度
function upload(next) {
const slice = file.slice(start, end)
const formData = new FormData()
formData.append("file", slice)
formData.append("size", fileSize)
formData.append("start", start)
formData.append("pos", pos)
axios.post(UploadUrl, formData, {
headers: {
'Content-Type': 'multipart/form-data'
}
}).then(res => {
console.log(res.data)
pos += sliceSize
start += sliceSize
end += sliceSize
if (start < fileSize) {
upload(next)
} else {
next()
}
}).catch(err => {
console.error(err)
})
}
return new Promise((resolve, reject) => {
// 上传之前请求后端接口获取Pos值
axios.get(UploadUrl)
.then(res => {
pos = res.data.pos
start = pos
end = pos + sliceSize
upload(() => {
// 上传完成时通知后端
const data = { pos: fileSize }
axios.post(UploadUrl, data)
.then(res => {
resolve(res.data)
}).catch(err => {
reject(err)
})
})
}).catch(err => {
console.error(err)
reject(err)
})
})
}
在组件中调用uploadFile
函数:
import React, { useState } from 'react'
function Upload() {
const [file, setFile] = useState(null)
function handleFileChange(e) {
setFile(e.target.files[0])
}
function handleUpload() {
if (file) {
uploadFile(file)
.then(res => {
console.log(res)
}).catch(err => {
console.error(err)
})
}
}
return (
<div>
<input type="file" onChange={handleFileChange} />
<button onClick={handleUpload}>上传文件</button>
</div>
)
}
后端代码实现
后端实现需要解析multipart/form-data格式的请求,获取上传的文件信息。同时,需要将上传的文件进行合并。
文件上传时需要按照如下步骤进行:
-
用户上传文件时,服务器会生成一个初始文件名。
-
请求接口时,判断上传的文件是否存在,如果存在,返回当前上传的位置;如果不存在,将初始文件名和文件大小记录下来,返回0作为上传位置。
-
前端上传文件时,每上传一段,后端就获取该段大小,然后将该段数据追加到临时文件末尾。
-
当整个文件上传完成时,将临时文件名更改为正式文件名,完成上传。
下面是后端处理上传的代码示例:
const path = require('path')
const fs = require('fs')
const pump = require('mz-modules/pump')
const UPLOAD_DIR = path.join(__dirname, 'upload') // 上传文件存储的目录
class UploadController extends Controller {
// 获取上传进度
async index() {
const { ctx } = this
const fileName = ctx.query.name
const filePath = path.join(UPLOAD_DIR, fileName)
if (fs.existsSync(filePath)) {
const stat = fs.statSync(filePath)
const pos = Number(ctx.query.pos) // 当前文件上传的位置
ctx.body = { pos: pos + stat.size }
} else {
ctx.body = { pos: 0 }
}
}
// 处理文件上传
async upload() {
const { ctx } = this
const stream = await ctx.getFileStream()
const fileName = stream.filename
const filePath = path.join(UPLOAD_DIR, fileName)
// 如果文件已经存在,则返回当前上传的位置
if (fs.existsSync(filePath)) {
const pos = Number(ctx.headers['x-pos'])
const stat = fs.statSync(`${UPLOAD_DIR}/${fileName}`)
ctx.body = { pos: pos + stat.size }
return
}
// 如果文件不存在,创建初始文件
const size = Number(ctx.headers['x-size'])
const ws = fs.createWriteStream(`${UPLOAD_DIR}/${fileName}`, { flags: 'w+' })
ws.write('0')
ws.end()
// 记录文件信息
ctx.service.file.create({ name: fileName, size })
// 上传文件并合并
ctx.set("Accept-Ranges", "bytes")
let pos = 0
const rs = fs.createReadStream(filePath, { start: pos, end: pos + stream.length - 1 })
await pump(rs, ws)
pos += stream.length
ctx.body = stream.fields // 返回其他字段
}
}
示例说明
示例一
我们在前端上传文件时,每次上传成功后,都会在控制台打印上传结果。如下是上传成功的日志:
{ pos: 1048576 }
{ pos: 2097152 }
{ pos: 3145728 }
{ pos: 4194304 }
{ pos: 5242880 }
{ pos: 6291456 }
{ pos: 7340032 }
这些记录表示上传文件时已经上传的总大小。在后续上传时,需要传入这个值,以便后端可以根据这个值继续上传。
示例二
文件上传完成后,后端需要将所有片段的内容进行合并,存储成一个完整的文件。上传完成后,我们可以在上传目录中查看文件是否上传成功。
将上传的小文件合并成一个完整的文件,可以使用NodeJS的stream流进行操作。在我们的代码实现中,我们使用pump模块将数据流从一个可读流传输到一个可写流中。
完整代码:https://github.com/LearnExpert/react-eggjs-resumable-upload-example
总结
通过上述代码实现,我们了解了如何使用React和EggJS实现断点续传。在实现过程中,主要通过HTTP请求的headers中带上请求的文件的断点位置,后端接口返回断点的位置,在上传文件时从断点位置处继续上传。同时,后端也需要将上传的多个文件进行拼接。通过这些实现,便完成了一个断点续传功能的实现。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:React+EggJs实现断点续传的示例代码 - Python技术站