如何使用egg.js写文件上传后端代码

9 篇文章 2 订阅

插件:egg-oss

安装:npm i egg-oss --save
文档:https://www.npmjs.com/package/egg-oss

配置:config/plugin.js

oss: {
    enable: true,
    package: 'egg-oss',
  }

配置:config/config.default.js

config.oss = {
    client: {
      accessKeyId: 'LTAI4DS1yG3E9z3dGijcqEvv',
      accessKeySecret: 'cwFVsXF5S5n75a8NZIz0IS2EO5pqvl',
      bucket: 'demo-mp3',
      endpoint: 'oss-cn-shenzhen.aliyuncs.com',
      timeout: '60s',
    },
  };
  
  // 上传格式和大小限制
config.multipart = {
    // fileSize: '50mb',
    fileSize: 1048576000,
    // mode: 'stream',
    mode: "file",
    fileExtensions: [
      // images
      '.jpg', '.jpeg', // image/jpeg
      '.png', // image/png, image/x-png
      '.gif', // image/gif
      '.bmp', // image/bmp
      '.wbmp', // image/vnd.wap.wbmp
      '.webp',
      '.tif',
      '.psd',
      // text
      '.svg',
      '.js', '.jsx',
      '.json',
      '.css', '.less',
      '.html', '.htm',
      '.xml',
      // tar
      '.zip',
      '.gz', '.tgz', '.gzip',
      // video
      '.mp3',
      '.mp4',
      '.avi',
    ],
  };

创建数据迁移表

npx sequelize migration:generate --name=file

1.执行完命令后,会在database / migrations / 目录下生成数据表迁移文件,然后定义

'use strict';

module.exports = {
  up: (queryInterface, Sequelize) => {
    const { INTEGER, STRING, DATE, ENUM, TEXT } = Sequelize;
    return queryInterface.createTable('file', {
      id: {
        type: INTEGER(20),
        primaryKey: true,
        autoIncrement: true
      },
      name: {
        type: STRING(100),
        allowNull: false,
        defaultValue: '',
        comment: '文件名'
      },
      ext: {
        type: STRING(50),
        allowNull: true,
        defaultValue: '',
        comment: '文件扩展名'
      },
      md: {
        type: STRING,
        allowNull: true,
        defaultValue: '',
        comment: '文件MD5'
      },
      file_id: {
        type: INTEGER,
        allowNull: false,
        defaultValue: 0,
        comment: '父级id'
      },
      user_id: {
        type: INTEGER,
        allowNull: false,
        defaultValue: 0,
        comment: '用户id',
        references: {
          model: 'user',
          key: 'id'
        },
        onDelete: 'cascade',
        onUpdate: 'restrict', // 更新时操作
      },
      size: {
        type: INTEGER,
        allowNull: false,
        defaultValue: 0,
        comment: '文件大小'
      },
      url: {
        type: STRING,
        allowNull: true,
        defaultValue: '',
        comment: '图片真实url'
      },
      isdir: {
        type: INTEGER,
        allowNull: false,
        defaultValue: 0,
        comment: '是否为文件夹',
      },
      created_time: DATE,
      updated_time: DATE,
    });
  },

  down: (queryInterface, Sequelize) => {
    return queryInterface.dropTable('file');
  }
};
  • 执行 migrate 进行数据库变更
npx sequelize db:migrate

模型创建

// app/model/file.js
module.exports = app => {
    const { STRING, INTEGER, DATE, ENUM, TEXT } = app.Sequelize;

    const File = app.model.define('file', {
        id: {
            type: INTEGER(20),
            primaryKey: true,
            autoIncrement: true
        },
        name: {
            type: STRING(100),
            allowNull: false,
            defaultValue: '',
            comment: '文件名'
        },
        ext: {
            type: STRING(50),
            allowNull: true,
            defaultValue: '',
            comment: '文件扩展名'
        },
        md: {
            type: STRING,
            allowNull: true,
            defaultValue: '',
            comment: '文件MD5'
        },
        file_id: {
            type: INTEGER,
            allowNull: false,
            defaultValue: 0,
            comment: '父级id'
        },
        user_id: {
            type: INTEGER,
            allowNull: false,
            defaultValue: 0,
            comment: '用户id',
            references: {
                model: 'user',
                key: 'id'
            },
            onDelete: 'cascade',
            onUpdate: 'restrict', // 更新时操作
        },
        size: {
            type: INTEGER,
            allowNull: false,
            defaultValue: 0,
            comment: '文件大小'
        },
        url: {
            type: STRING,
            allowNull: true,
            defaultValue: '',
            comment: '图片真实url'
        },
        isdir: {
            type: INTEGER,
            allowNull: false,
            defaultValue: 0,
            comment: '是否为文件夹',
        },
        created_time: DATE,
        updated_time: DATE,
    });

    // 删除后
    File.afterBulkDestroy(async (data, option) => {
        console.log('删除后', data.where);

        let files = await app.model.File.findAll({
            where: {
                file_id: data.where.id,
                user_id: data.where.user_id,
                isdir: 1
            }
        });

        let ids = files.map(item => item.id);

        if (ids.length > 0) {
            app.model.File.destroy({
                where: {
                    id: ids,
                    user_id: data.where.user_id
                }
            });
        }

    });

    return File;
};

控制器:app/controller/file.js

// 引入
    const fs = require('fs');
    const path = require('path');

    // 上传
    async upload() {
        const { ctx, app } = this;
        const currentUser = ctx.authUser;

        if (!ctx.request.files) {
            return ctx.apiFail('请先选择上传文件');
        }

        ctx.validate({
            file_id: {
                type: "int",
                required: true,
                defValue: 0,
                desc: 'file_id'
            },
        });

        const file_id = ctx.query.file_id;

        // 文件id是否存在
        if (file_id > 0) {
            // 目录是否存在
            await this.service.file.isDirExist(file_id);
        }

        const file = ctx.request.files[0];
        const name = 'egg-oss-demo/' + ctx.genID(10) + path.extname(file.filename);

        // 验证用户剩余内存是否满足要求 
        let s = await (new Promise((resolve, reject) => {
            fs.stat(file.filepath, (err, stats) => {
                resolve((stats.size / 1024).toFixed(1));
            });
        }));

        if ((currentUser.total_size - currentUser.used_size) < s) {
            return ctx.apiFail('你的可用内存不足');
        }

        let result;
        try {
            result = await ctx.oss.put(name, file.filepath);
        } catch (err) {
            console.log(err);
        }

        if (result) {
            // 写入数据表
            let addData = {
                name: file.filename,
                ext: file.mimeType,
                md: result.name,
                file_id,
                user_id: currentUser.id,
                size: parseInt(s),
                isdir: 0,
                url: result.url
            };

            if (file_id > 0) {
                addData.file_id = file_id;
            }

            let res = await app.model.File.create(addData);

            // 更新user表的使用内存
            currentUser.used_size = currentUser.used_size + parseInt(s);
            currentUser.save();

            return ctx.apiSuccess(res);
        }

        ctx.apiFail('上传失败');
    }

服务:app/service/file.js

  // 目录是否存在
    async isDirExist(id) {
        let f = await this.app.model.File.findOne({
            where: {
                id,
                // 当前用户id
                user_id: this.ctx.authUser.id,
                isdir: 1
            }
        });

        if (!f) {
            return this.ctx.throw(404, '目录不存在');
        }

        return f
    }

扩展:app/extend/context.js

  // 生成唯一id
    genID(length) {
        return Number(Math.random().toString().substr(3, length) + Date.now()).toString(36);
    }

路由:app/router.js

router.post('/upload', controller.file.upload);

喜欢本章文章的,麻烦给个关注和点赞,你的一个小小的点赞,是作者无限进步的动力!!

微信公众号,更多资源等你来!!

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

@逆风boy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值