React+node 图片剪裁上传,集成本地存储和阿里云OSS

本文涉及的产品
对象存储 OSS,20GB 3个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
对象存储 OSS,内容安全 1000次 1年
简介: 最近一直在调研图片上传阿里云oss功能,上篇文章主要讲述了阿里云oss大文件分片、断点续传。这篇文章是在原有基础上,前端加了图片剪裁、后端加了本地存储功能。

前言

最近一直在调研图片上传阿里云oss功能,上篇文章主要讲述了阿里云oss大文件分片、断点续传。这篇文章是在原有基础上,前端加了图片剪裁、后端加了本地存储功能。

先看效果
图片剪裁上传 -original-original.gif
技术栈

  1. 前端:react+Ts+antd
  2. 后端:node+koa+koa-body+ali-oss

功能

  1. 图片剪裁(antd剪裁组件
  2. 本地文件上传(antd上传组件+axios
  3. 文件存储本地(node+koa-body
  4. 文件存储阿里oss(node+ali-oss

    核心代码

    环境配置

    引入依赖环境

    使用koa搭建node服务,搭配koa周边依赖包开发起来更方便,详细依赖环境如下:
    const app = new (require('koa'))(); // koa 
    const router = require('koa-Router')();  // koa 路由
    const cors = require('@koa/cors') // 运行koa中跨域 
    const OSS = require('ali-oss') // 阿里云oss-sdk
    const {
         
         koaBody} = require('koa-body'); // 处理请求体中间件、用于解析json、表单 | 包含file内容请求,会生成临时文件,将文件信息添加到ctx.request对象属性,通过ctx.request.files获取
    const path = require('path'); // 对文件路径的操作
    const staticServe = require('koa-static') // 使上传的文件能在浏览器中访问
    const fs = require('fs') // 文件的读写操作 
    const PORT = '9000' // 端口
    

    配置阿里云oss

    const client = new OSS({
         
         
     // yourregion填写Bucket所在地域。以华东1(杭州)为例,Region填写为oss-cn-hangzhou。
     region: 'oss-cn-beijing',
     // 阿里云账号AccessKey拥有所有API的访问权限,风险很高。强烈建议您创建并使用RAM用户进行API访问或日常运维,请登录RAM控制台创建RAM用户。
     accessKeyId: 'xxx',
     accessKeySecret: 'xx',
     // 填写Bucket名称。
     bucket: 'xxx'
    });
    

    使用中间件

    实现本地上传、解决跨域等功能,需使用koa中间件完成功能
    ``php // __dirname node中全局变量,当前文件所在目录 // 上传本地目录 const UPLOAD_PATH = path.join(__dirname,'public/') // 上传文件后的地址,用于拼接图片名称,回显前端 const UPLOAD_URL =http://localhost:${PORT}`
    // 使用跨域中间件
    app.use(cors())

//应用koabody中间件,处理文件上传操作,生成临时文件路径
app.use(koaBody({
multipart:true ,// 解析文件格式内容
formidable:{
// 上传文件的存储的位置
uploadDir:UPLOAD_PATH,
keepExtensions:true // 保留文件的扩展名
}
}))
// app.use(bodyParser());
app.use(staticServe(UPLOAD_PATH))
app.use(router);
// 启动服务
app.listen(PORT,()=>{
console.log('启动成功 9000')
});

## 阿里云分片上传
### 接口代码
```javascript
router.post('/upload_oss',async(ctx)=>{
    console.log('请求了',ctx.request.files.file)
    console.log('上传oss upload_oss',ctx.request)
    const file = ctx.request.files.file
    let result  = await multipartUpload(file) 
    ctx.body={
        msg:"请求成功了",
        result
    }
})

分片操作

// 上传进度
const progress = (p, _checkpoint) => {
   
   
    // Object的上传进度。
    console.log('分片进度',p); 
    // 分片上传的断点信息。
    // console.log(_checkpoint); 
  };

async function multipartUpload(file) {
   
   
    try {
   
   
      // 依次填写Object完整路径(例如exampledir/exampleobject.txt)和本地文件的完整路径(例如D:\\localpath\\examplefile.txt)。Object完整路径中不能包含Bucket名称。
      // 如果本地文件的完整路径中未指定本地路径(例如examplefile.txt),则默认从示例程序所属项目对应本地路径中上传文件。
      const result = await client.multipartUpload(file.originalFilename, file.filepath, {
   
   
        progress, // 如无需进度实时回显,可不配置
        // headers,
        // 指定meta参数,自定义Object的元信息。通过head接口可以获取到Object的meta数据。
      });
      console.log(result); 
      return result
    } catch (e) {
   
   
      // 捕获超时异常。

      console.log('捕获超时异常。',e);
    }
  }

本地文件上传

存储本地文件两种方式

一、renameSync(移动临时文件)

fs.renameSync(ctx.request.files.file.filepath, filePath);

优点:直接使用fs.renameSync,一次操作即可重命名或移动文件,非常简洁和高效。
缺点:fs.renameSync 是一个同步方法,会直接阻塞代码执行,直到文件操作完成

二、创建文件流存储

const reader = fs.createReadStream(ctx.request.files.file.filepath);
const writer = fs.createWriteStream(filePath);
reader.pipe(writer);

优点:可以同时处理多个文件的上传,不会阻塞代码执行。
缺点:在处理单个文件时可能略微复杂。

renameSync 存储单文件

// 请求中的第二个参数是请求中间件函数,可用于请求前的业务操作 
router.post('/upload_local',async(ctx,next)=>{
   
   

    const filePath = UPLOAD_PATH+`/${
     
     ctx.request.files.file.originalFilename}`
    fs.renameSync(ctx.request.files.file.filepath, filePath);
    await next()
},async(ctx)=>{
   
   
    ctx.body={
   
   
        msg:"请求成功了", 
        imgUrl:UPLOAD_URL+`/${
     
     ctx.request.files.file.originalFilename}` 
    }
})

常见问题

multer存储文件名乱码怎么办?

在第一版代码中,采用的是multer存储文件,上传文件是中文命名,multer中无法解析格式,导致文件名乱码。
解决方式:将使用 Latin-1 编码的文件名转换为 UTF-8 编码
示例代码:

// 文件名称命名
    filename:function(req,file,cb){
   
   
        console.log('文件名称命名')  
        const decodedName = Buffer.from(file.originalname, "latin1").toString(
            "utf8"
          );
        console.log('decodedName',decodedName)
        cb(null,decodedName)
    }

Buffer.from(file.originalname, "latin1")使用 Buffer.from 方法将以 Latin-1 编码的 file.originalname 字符串转换为一个 Buffer 对象。Latin-1 是一种字符编码,也称为 ISO-8859-1。
toString("utf8")使用 toString 方法将 Buffer 对象转换回字符串,指定目标编码为 UTF-8。这将将 Latin-1 编码的字符串转换为 UTF-8 编码的字符串。

Koa接收不到file对象内容

正常情况下,引入koa-body中间件后,可以获取到前端表单数据,如下:
image.png
如果获取不到,大概率是 routes 代码书写顺序错乱导致
⚠ 中间件的应用需要写在routes前面才可以
正常执行顺序如下:

const koaBody  = require('koa-body')({
   
   multipart: true});
app.use(koaBody); //中间件的应用需要写在routes前面才可以
app.use(router.routes())

koa-body 和 koa-bodypaser不兼容

在第一版代码中,使用 koa-bodypaser + multer来存储本地文件,koa-body+ali-oss上传阿里云存储。

这两个功能单独使用是没有问题,但是服务端同时定义两个接口,则会出现阿里云oss或本地存储失败。百度搜了一圈没找到解决方案,最终果断弃坑multer, 使用koa-body 来解析请求体内容.

koa-body不仅可以解析请求体内容、还可以生产文件临时路径、方便存储操作

  • koa-body 用于解析 请求中的 formData 文件内容格式、 json、buffer
  • koa-bodypaser 用于解析 非文件内容数据,比如json、表单
  • 如果node业务中涉及 以上两种情况下,建议使用koa-body。
相关实践学习
借助OSS搭建在线教育视频课程分享网站
本教程介绍如何基于云服务器ECS和对象存储OSS,搭建一个在线教育视频课程分享网站。
相关文章
|
2月前
|
存储 Java 开发工具
【三方服务集成】最新版 | 阿里云OSS对象存储服务使用教程(包含OSS工具类优化、自定义阿里云OSS服务starter)
阿里云OSS(Object Storage Service)是一种安全、可靠且成本低廉的云存储服务,支持海量数据存储。用户可通过网络轻松存储和访问各类文件,如文本、图片、音频和视频等。使用OSS后,项目中的文件上传业务无需在服务器本地磁盘存储文件,而是直接上传至OSS,由其管理和保障数据安全。此外,介绍了OSS服务的开通流程、Bucket创建、AccessKey配置及环境变量设置,并提供了Java SDK示例代码,帮助用户快速上手。最后,展示了如何通过自定义starter简化工具类集成,实现便捷的文件上传功能。
【三方服务集成】最新版 | 阿里云OSS对象存储服务使用教程(包含OSS工具类优化、自定义阿里云OSS服务starter)
|
5月前
|
前端开发 对象存储
oss的断点续传在react中的应用
使用阿里云OSS的`multipartUpload`可自动管理文件切片与断点续传吗?测试时,上传一半中断(如刷新页面或重启浏览器),再续应从断点处继续。
99 10
|
5月前
|
分布式计算 DataWorks 调度
DataWorks产品使用合集之在使用MaxCompute进行数据集成同步到OSS时,出现表名和OSS文件名不一致且多了后缀,该如何处理
DataWorks作为一站式的数据开发与治理平台,提供了从数据采集、清洗、开发、调度、服务化、质量监控到安全管理的全套解决方案,帮助企业构建高效、规范、安全的大数据处理体系。以下是对DataWorks产品使用合集的概述,涵盖数据处理的各个环节。
|
4月前
|
缓存 JavaScript Serverless
阿里云云效产品使用合集之如何在Serverless Devs阶段指定Node.js版本
云效作为一款全面覆盖研发全生命周期管理的云端效能平台,致力于帮助企业实现高效协同、敏捷研发和持续交付。本合集收集整理了用户在使用云效过程中遇到的常见问题,问题涉及项目创建与管理、需求规划与迭代、代码托管与版本控制、自动化测试、持续集成与发布等方面。
|
6月前
|
敏捷开发 缓存 测试技术
阿里云云效产品使用问题之构建Vue3项目,怎么让node_modules缓存下来
云效作为一款全面覆盖研发全生命周期管理的云端效能平台,致力于帮助企业实现高效协同、敏捷研发和持续交付。本合集收集整理了用户在使用云效过程中遇到的常见问题,问题涉及项目创建与管理、需求规划与迭代、代码托管与版本控制、自动化测试、持续集成与发布等方面。
|
5月前
|
移动开发 运维 JavaScript
阿里云云效操作报错合集之遇到Node.js的内存溢出问题,该怎么办
本合集将整理呈现用户在使用过程中遇到的报错及其对应的解决办法,包括但不限于账户权限设置错误、项目配置不正确、代码提交冲突、构建任务执行失败、测试环境异常、需求流转阻塞等问题。阿里云云效是一站式企业级研发协同和DevOps平台,为企业提供从需求规划、开发、测试、发布到运维、运营的全流程端到端服务和工具支撑,致力于提升企业的研发效能和创新能力。
|
5月前
|
JavaScript Shell 应用服务中间件
阿里云云效操作报错合集之Node.js构建报错,该如何排查问题
本合集将整理呈现用户在使用过程中遇到的报错及其对应的解决办法,包括但不限于账户权限设置错误、项目配置不正确、代码提交冲突、构建任务执行失败、测试环境异常、需求流转阻塞等问题。阿里云云效是一站式企业级研发协同和DevOps平台,为企业提供从需求规划、开发、测试、发布到运维、运营的全流程端到端服务和工具支撑,致力于提升企业的研发效能和创新能力。
|
5月前
|
资源调度 前端开发 开发工具
阿里云云效操作报错合集之Node-Sass模块在构建过程中,出现报错"ENOENT: no such file or directory, scandir ",该如何处理
本合集将整理呈现用户在使用过程中遇到的报错及其对应的解决办法,包括但不限于账户权限设置错误、项目配置不正确、代码提交冲突、构建任务执行失败、测试环境异常、需求流转阻塞等问题。阿里云云效是一站式企业级研发协同和DevOps平台,为企业提供从需求规划、开发、测试、发布到运维、运营的全流程端到端服务和工具支撑,致力于提升企业的研发效能和创新能力。
|
5月前
|
JavaScript IDE 持续交付
阿里云云效产品使用合集之如何配置 Node.js构建任务让其在Windows环境中进行
云效作为一款全面覆盖研发全生命周期管理的云端效能平台,致力于帮助企业实现高效协同、敏捷研发和持续交付。本合集收集整理了用户在使用云效过程中遇到的常见问题,问题涉及项目创建与管理、需求规划与迭代、代码托管与版本控制、自动化测试、持续集成与发布等方面。
|
6月前
|
存储 DataWorks 关系型数据库
DataWorks产品使用合集之在使用数据集成中的同步任务从mysql同步表到oss,存储为csv时,最终生成的文件中没有表头,这个属性可以在哪里配置么
DataWorks作为一站式的数据开发与治理平台,提供了从数据采集、清洗、开发、调度、服务化、质量监控到安全管理的全套解决方案,帮助企业构建高效、规范、安全的大数据处理体系。以下是对DataWorks产品使用合集的概述,涵盖数据处理的各个环节。