在本地mock文件接口和调试

实际开发项目,经常会遇到文件相关的接口,比如文件上传和下载,这里针对前端总结一些 mock 文件接口在本地调试的方法

文件下载

基于Node.js快速启动一个 web 服务,借助中间件 koa-send 实现下载功能

const path = require('path');
const Koa = require('koa');
const staticServe = require('koa-static');
const router = require('koa-router')();
const send = require('koa-send');
const app = new Koa();
const staticPath = '/static';
const absolutePath = path.join(__dirname + staticPath);

// 指定静态目录
app.use(staticServe(absolutePath));
app.use(async (ctx) => {
    ctx.body = 'hello word'
});
router.get('/file/:name', async (ctx){
    const name = ctx.params.name;
    const path = `static/${name}`;
    ctx.attachment(path);
    await send(ctx, path);
})

app.use(router.routes()).use(router.allowedMethods());
app.listen(3000, () => {
  console.log('server is listening in 3000...');
})

koa-send 内部其实用了 fs.createReadStream,以块的形式发送出去,这样前端可以更快地接收数据。fs.readFile 还会将整个文件加载到内存中

前端调用接口

fetch

fetch()
  .then((res) => {
    return res.blob();
    // txt、base64或其他文本类的文件可用以下方式解析
    // return res.text()
  })
  .then((res) => {
    // 下载文件示例
    let url = URL.createObjectURL(new Blob([res]));
    let link = document.createElement("a");
    link.style.display = "none";
    link.href = url;
    link.download = `image.png`;
    document.body.appendChild(link);
    link.click();
    // 解析base64
    // const data = window.atob(res)
  });

如果是 axios,需要额外配置 responseType: 'blob',其实也就是 XMLHttpRequest 的配置

类似的中间件有 koa-sendfile,主要是解决中文编码的问题

文件上传

前端主要是通过表单来上传文件,但是文件数据在服务器端并不能像普通参数一样通过 ctx.request.body 获取。这里我们可以借助 koa-body 实现上传功能,该中间件的作用主要是将文件数据拼接到 ctx.request.body.files.file

const koaBody = require('koa-body');
// ...
app.use(koaBody({
    multipart: true,
    // formidable: {
    //   默认限制只有2M,超过会报错
    //   maxFileSize: 100*1024*1024
    // }
}));
router.post('/upload', async (ctx){
    const file = ctx.request.body.files.file;
    const reader = fs.createReadStream(file.path);
    const upStream = fs.createWriteStream(`upload/${file.name}`);
    reader.pipe(upStream);
    return ctx.body = '上传成功';
})
// ...

前端拼接文件数据

const formData = new FormData();
formData.append("file", input.files[0]);
formData.append("user", "Lucas");
fetch(url, {
  method: "POST",
  body: formData,
});

不起 web 服务

纯 js 由于浏览器的安全考虑是没法直接操作文件的,如果不想启动服务,还可以借助文件选择或拖拽来模拟读取文件接口。以下示例基于 React Hook(只是提供获取文件的方法,具体使用还需要自行验证)

文件选择:

const fileInputRef = useRef();
// ...
// 监听文件选择
fileInputRef.current.onchange = function (event) {
  const file = event.target.files[0];
};
// ...
<input type="file" name="test" ref={fileInputRef} />;

拖拽:

const dragDropRef = useRef();
// ...
// 监听
dragDropRef.current.addEventListener(
  "drop",
  function (event) {
    const file = event.dataTransfer.files[0];
  },
  false
);
// ...
<div ref={dragDropRef} />;

更多前端处理二进制数据的方法可以参考 https://blog.zhouweibin.top/js/%E4%BA%8C%E8%BF%9B%E5%88%B6%E6%95%B0%E6%8D%AE/