React+Koa实现文件上传的示例

最近在写毕设的时候,涉及到了一些文件上传的功能,其中包括了普通文件上传,大文件上传,断点续传等等

服务端依赖

koa(node.js框架)

koa-router(Koa路由)

koa-body(Koa body 解析中间件,可以用于解析post请求内容)

koa-static-cache(Koa 静态资源中间件,用于处理静态资源请求)

koa-bodyparser(解析 request.body 的内容)

后端配置跨域

app.use(async (ctx, next) => { ctx.set('Access-Control-Allow-Origin', '*'); ctx.set( 'Access-Control-Allow-Headers', 'Content-Type, Content-Length, Authorization, Accept, X-Requested-With , yourHeaderFeild', ); ctx.set('Access-Control-Allow-Methods', 'PUT, POST, GET, DELETE, OPTIONS'); if (ctx.method == 'OPTIONS') { ctx.body = 200; } else { await next(); } });

后端配置静态资源访问 使用 koa-static-cache

// 静态资源处理 app.use( KoaStaticCache('./pulbic', { prefix: '/public', dynamic: true, gzip: true, }), );

后端配置requst body parse 使用 koa-bodyparser

const bodyParser = require('koa-bodyparser'); app.use(bodyParser());

前端依赖

React

Antd

axios

正常文件上传
后端

后端只需要使用 koa-body 配置好options,作为中间件,传入router.post('url',middleware,callback)即可

后端代码

// 上传配置 const uploadOptions = { // 支持文件格式 multipart: true, formidable: { // 上传目录 这边直接上传到public文件夹,方便访问 文件夹后面要记得加/ uploadDir: path.join(__dirname, '../../pulbic/'), // 保留文件扩展名 keepExtensions: true, }, }; router.post('/upload', new KoaBody(uploadOptions), (ctx, next) => { // 获取上传的文件 const file = ctx.request.files.file; const fileName = file.path.split('https://www.jb51.net/')[file.path.split('https://www.jb51.net/').length-1]; ctx.body = { code:0, data:{ url:`public/${fileName}` }, message:'success' } });

前端

我这里使用的是formData传递的方式,前端通过<input type='file'/> 来访问文件选择器,通过onChange事件 e.target.files[0] 即可获取选择的文件,而后创建FormData 对象将获取的文件formData.append('file',targetFile)即可

前端代码

const Upload = () => { const [url, setUrl] = useState<string>('') const handleClickUpload = () => { const fileLoader = document.querySelector('#btnFile') as HTMLInputElement; if (isNil(fileLoader)) { return; } fileLoader.click(); } const handleUpload = async (e: any) => { //获取上传文件 const file = e.target.files[0]; const formData = new FormData() formData.append('file', file); // 上传文件 const { data } = await uploadSmallFile(formData); console.log(data.url); setUrl(`${baseURL}${data.url}`); } return ( <div> <input type="file" onChange={handleUpload} style={{ display: 'none' }} /> <Button onClick={handleClickUpload}>上传小文件</Button> <img src=https://www.jb51.net/article/{url} /> </div> ) }

其他可选方法

input+form 设置form的aciton为后端页面,enctype="multipart/form-data",type=‘post'

使用fileReader读取文件数据进行上传 兼容性不是特别好

大文件上传

文件上传的时候,可能会因为文件过大,导致请求超时,这时候就可以采取分片的方式,简单来说就是将文件拆分为一个个小块,传给服务器,这些小块标识了自己属于哪一个文件的哪一个位置,在所有小块传递完毕后,后端执行merge 将这些文件合并了完整文件,完成整个传输过程

前端

获取文件和前面一样,不再赘述

设置默认分片大小,文件切片,每一片名字为 filename.index.ext,递归请求直到整个文件发送完请求合并

const handleUploadLarge = async (e: any) => { //获取上传文件 const file = e.target.files[0]; // 对于文件分片 await uploadEveryChunk(file, 0); } const uploadEveryChunk = ( file: File, index: number, ) => { console.log(index); const chunkSize = 512; // 分片宽度 // [ 文件名, 文件后缀 ] const [fname, fext] = file.name.split('.'); // 获取当前片的起始字节 const start = index * chunkSize; if (start > file.size) { // 当超出文件大小,停止递归上传 return mergeLargeFile(file.name); } const blob = file.slice(start, start + chunkSize); // 为每片进行命名 const blobName = `${fname}.${index}.${fext}`; const blobFile = new File([blob], blobName); const formData = new FormData(); formData.append('file', blobFile); uploadLargeFile(formData).then((res) => { // 递归分片上传 uploadEveryChunk(file, ++index); }); };

后端

后端需要提供两个接口

上传

将上传的每一个分块存储到对应name 的文件夹,便于之后合并

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wsdgdf.html