fields中的文件数据

上传过来的文件存放在ctx.request.fields里面,他是一个数组。

fields大概数据如下:

img: [
    File {
        _events: [Object: null prototype] {},
        _eventsCount: 0,
        _maxListeners: undefined,
        size: 86516,
        path: 'E:\\代码\\koa-web\\upload\\upload_13de2b1692363e80c2a838f1744ae9ce',
        name: 'QQ截图20200903154421.jpg',
        type: 'image/jpeg',
        hash: null,
        lastModifiedDate: 2020 - 09 - 04 T11: 09: 20.750 Z,
        _writeStream: [WriteStream],
        [Symbol(kCapture)]: false
    }
]

img为表单元素的name,注意,一定要设置name,不然获取不到值,然后img为一个数组。

其中path后段文本为上传过来的文件名,且没有格式后缀,文件名也是一个全局唯一值。

存储文件的话,一般都是存储upload_13de2b1692363e80c2a838f1744ae9ce这个名字,但是此时他在path里面,是一串路径,我们需要进行提取。

提取文件名

const path = require("path");


const name = path.basename(ctx.request.fields["img"][0].path);

console.log(name)   
// upload_13de2b1692363e80c2a838f1744ae9ce

我们需要引入path这个模块,利用的他basename方法,把文件路径传进去,就能得到文件名了

过滤

由于使用了koa-better-body插件,实际上会有一个问题,哪怕用户不上传文件,body已经会创建一个size为0的文件丢到upload文件夹中,所以,不管上传不上传文件,body都会有文件,所以我们进行过滤。

分两步:

  1. 删除文件夹中的文件
  2. 文件名过滤
const fs = require("promise-fs");

const imgArr = ctx.request.fields["img"];

imgArr.forEach(async img => {
    if (!img.size) {
        await fs.unlink(img.path);
    }
});

const name = imgArr.filter(img => {
   return img.size
});

console.log(name)

我们使用promisi-fs插件进行删除文件,因为是个异步的,所以不能放在filter函数里面一起,因为filter是一个需要立即返回数据的函数,不然就会出现问题,所以我们在筛选之前做个遍历,把size等于0的文件删除。

然后再通过筛选,选出size大于0的文件数组。

过滤的一些问题

对于这个koa-better-body其实也不是每次上传,没有上传文件就会生成一个0字节的文件,有时候他可能没有,没有的话,我们删除文件的操作就可能出错,所以我们要对删除操作做一个try-catch包裹

imgArr.forEach(async img => {
    if (!img.size) {
      try {
        await fs.unlink(img.path);
      }catch(e){
        console.log(e);
      }  
    }
});

数据库写入的方法(数据库方法补充)

由于我们可能会有很多个字段的上传,比如title,subtitle,还有更多,我们可以将字段名保存为一个数组。

const nameArr = ["title","subtitle"];

保存为数组之后,我们需要考虑到数据,所以数据也必须是一个数组,且顺序就等于name数组的顺序,值是对应的。

这样我们就可以使用这种写法

ctx.db.query(`UPDATE 表名 SET ${nameArr.map(n => n+"=?").join(",")} WHERE id=?`,[...valueArr,id]);

利用map方法,map是遍历一个数组,再把修改好的值生成一个新的数组,这样我们就生成了一个[name=?,name=?]这种形式的数组,再通过join转为逗号字符。

然后对应的值通过es6语法释放在一个数组里面,后面还能在添加新值。

这种写法,可以大大的提供多个值修改时的写法,想对来着,这种用法,其实还有有一个前缀,就是name和value都是数组且一一对应的。

为了达到这种情况,我们可以写一个复用的方法,中间件啥的,对name和value做一个统一操作,然后通过ctx对象数据沟通。

修改没有填写内容的字段

比如说有一个字段,他不是必填的,然后用户上传时这个字段的值为空,那么会有两种情况,要么用户就是删除内容,要么是漏写了,不想写,想用旧的。

对于这种情况我们也需要考虑,我们是用新值呢还是旧值,特别是上传文件的那种,用户不可能每次修改都选择一个文件,有可能这个文件他不想改,那么这个文件字段就是空了。

所以可以这样,如果是文本类型,用户传什么就更新什么,空也好,不空也好。

如果文件类型,那么就判断是否为空,空的话就不进行更新。

文件读取

之前我们设置的静态文件读取都是通过static模块,自动读取static文件夹里的文件,但是如果上传的文件,他是在upload目录,所以static并不适用,所以我们需要创建一个专门读取upload文件夹的路由

为什么不放在static里面呢,因为static里面任何人都能拿到,如果是一个vip文件,那不是普通用户也能获取了,所以我们创建一个路由,在路由里面我们可以进行判断。

router.get("/upload",async ctx=>{
  
});

在这个路由路由里面我们进行返回数据,这里我们还需要使用到一个插件

koa-send

安装

npm i koa-send -D

使用

const send=require('koa-send');
const {upload_dir}=require('../config');


router.get("/upload/:img",async ctx=>{
  let img=ctx.params.img;

  await send(ctx, img, {
    maxage: 60*86400*1000,
    immutable: true,
    root: upload_dir
  });
});

send他也是一个异步的方法,用于发送文件,他有三个参数,第一个为ctx对象,第二个为要发送的文件名,第三个为配置参数。

  1. maxage 缓存时间 ms
  2. immutable 是否长期不变,如果文件不改的,可以开这个
  3. root 这个为根目录,也就是upload路径,我们从config里面拿到路径
  4. gzip 是否进行压缩,默认是true,不要可以关闭

图片的文件名,他是没有后缀格式的,因为我们上传的时候只是把他的文件名存在sql,现在读取的时候,也没有设置后缀格式,但是浏览器依旧可以读取到图片,并且显示。

文件后缀

我们可以通过name属性拿到后缀

let format = name.split(".");
format=formatp[format.length-1];

这样,我们可以在存的时候拿到这个后缀,然后对上传过来的文件进行修改,也可以通过这个后缀判断是否是我们需要的文件格式。

分类: Node 标签: nodekoa

评论

暂无评论数据

暂无评论数据

目录