amis 文件上传 大文件分块上传

amis 图片/文件上传组件

receiver:参数配置为上传接口。

{"type": "input-image", // "type": "input-file","label": "照片","name": "url", "imageClassName": "r w-full","receiver": "/lbserver/api/FileUpload/upload/mPersonnelInfo/Images/${TIMESTAMP(NOW(),'x')}","accept": ".jpeg, .jpg, .png, .gif","fixedSize": false,"hideUploadButton": false,"autoUpload": true,"compress": false,"compressOptions": {},"crop": false
}

amis分块上传:

分块上传所需的处理如下流程图所示:

文件上传文件如果过大的话,如果不加任何处理,这个请求就会一直处于PENDING状态(最后肯定是超时的)

pending(挂起):网络处于挂起状态,指发送的请求是“进行中”的状态,但还没有接到服务端的响应,一旦服务端做出响应,时间将被更新为总运行时间。

0、前端amis分片逻辑如下:(了解即可,一般分片逻辑无需自己实现,用现成组件库)

• 由于前端已有 Blob Api 能操作文件二进制,因此最核心逻辑就是前端运用 Blob Api 对大文件进行文件分片切割,将一个大文件切成一个个小文件,然后将这些分片文件一个个上传。

• 现在 http 请求基本是 1.1 版本,浏览器能够同时进行多个请求,通过Promise进行异步并发控制处理。

• 当前端将所有分片上传完成之后,前端再通知后端进行分片合并成文件。

amis/src/renderers/Form/InputFile.tsx

      //调用startChunkApi 成功后执行startChunk进行分块self._send(file, startApi).then(startChunk).catch(reject); async function startChunk(ret: Payload) {onProgress(startProgress);const tasks = getTasks(file); //根据chunkSize分块大小(默认5M)生成分块任务集合progressArr = tasks.map(() => 0);if (!ret.data) {throw new Error(__('File.uploadFailed'));}state = {key: (ret.data as any).key,uploadId: (ret.data as any).uploadId,loaded: 0,total: tasks.length};let results: any[] = [];while (tasks.length) {const res = await Promise.all(tasks.splice(0, concurrency).map(async task => {//根据concurrency 控制并行上传数量,默认是 3return await uploadPartFile(state, config)(task); //Blob.slice API进行分块 并调用chunkApi上传}));results = results.concat(res);}finishChunk(results, state);//finishChunkApi 结束分片}

1.amis分块上传参数配置

Amis上传组件如果文件过大,则可能需要使用分块上传,默认大于 5M(chunkSize 配置决定) 的文件是会自动开启,可以通过 useChunk 配置成 false 关闭。(不要手动配置useChunk:true,会导致只使用chunk切片上传)

{"type": "input-file","id": "u:dbd914e494e9","label": "File","name": "file","autoUpload": true,"uploadType": "fileReceptor","accept": "*","receiver": "/lbserver/api/FileUpload/upload/mProjectInfo/Images/${TIMESTAMP(NOW(),'x')}","startChunkApi": "/lbserver/api/FileUpload/startChunkApi","chunkApi": "/lbserver/api/FileUpload/chunkApi/upload/mProjectInfo/Images","finishChunkApi": "/lbserver/api/FileUpload/finishChunkApi/upload/mProjectInfo/Images","hidden": false,"btnLabel": "文件上传","submitType": "asUpload"
}

2.分块上传相关的三个后端接口(loopback4.0框架 文件上传基于multer):

multer中间件只处理 multipart/form-data 类型的表单数据的函数,主要用于上传文件。

Multer在解析完请求体后,会向request对象中添加一个body对象和一个file或files对象(上传多个文件时使用files对象 )。其中,body对象中包含所提交表单中的文本字段(如果有),而file(或files)对象中包含通过表单上传的文件。

import { inject, service } from '@loopback/core';
import {del,get,getModelSchemaRef,param,patch,post,Request,requestBody,response,Response,RestBindings,
} from '@loopback/rest';
import _ from 'lodash';
import { FILE_UPLOAD_SERVICE } from '../../keys';
import { FileUploadHandler } from '../../types';const moment = require('moment');
const SparkMD5 = require('spark-md5');
const util = require('util');
const mime = require('mime');
const fs = require('fs-extra');
const path = require('path');
const child_process = require('child_process');function getFilesAndFields(request: Request) {const uploadedFiles = request.files;const mapper = (f: globalThis.Express.Multer.File) => ({fieldname: f.fieldname,originalname:request.body && request.body.key && request.body.partNumber? `${request.body.key}-${request.body.partNumber}`: f.originalname,encoding: f.encoding,mimetype: f.mimetype,size: f.size,});let files: object[] = [];if (Array.isArray(uploadedFiles)) {files = uploadedFiles.map(mapper);} else {for (const filename in uploadedFiles) {files.push(...uploadedFiles[filename].map(mapper));}}return { files, fields: request.body };
}export class FileUploadController {constructor(@inject(FILE_UPLOAD_SERVICE) private handler: FileUploadHandler,) { }@post(`FileUpload/startChunkApi`)@response(200, {description: 'FileUpload model instance',content: { 'application/json': { schema: getModelSchemaRef(FileUpload) } },})async startChunkApi(@requestBody() pl: any): Promise<any> {let uploadId = generateUUID();let key = `${moment().format('X')}-${pl.filename}`;return {status: 0,data: {date: new Date(),uploadId: uploadId,key: key,},};}@post(`FileUpload/chunkApi/{upload}/{model}/{type}`)@response(200, {description: 'FileUpload model instance',content: { 'application/json': { schema: getModelSchemaRef(FileUpload) } },})async chunkApi(@param.path.string('upload') upload: string,@param.path.string('model') model: string,@param.path.string('type') type: string,@requestBody.file()request: Request,@inject(RestBindings.Http.RESPONSE) response: Response,): Promise<any> {// console.log(model, type);return new Promise<any>((resolve, reject) => {this.handler(request, response, err => {if (err) reject(err);else {let uploadId = request.body.uploadId; // id// let key = request.body.key;// let partNumber = request.body.partNumber;const f = getFilesAndFields(request);if (f.files && f.files.length > 0) {for (const i in f.files) {const m = f.files[i] as any;fs.mkdirpSync(path.resolve(`./public/${upload}/${model}/${type}/${uploadId}`),);const o_file = `./.sandbox/${m.originalname}`;let eTag = SparkMD5.hashBinary(fs.readFileSync(o_file, 'binary')); //不指定编码 返回buffer对象const m_file = `./public/${upload}/${model}/${type}/${uploadId}/${m.originalname}`;fs.rename(o_file, m_file, function (err: any) {if (err) {child_process.execSync(`mv ${o_file} ${m_file}`);console.log(err);}});const result = {name: m.originalname,eTag: eTag,};resolve({status: 0,msg: '',data: result,});}}}});});}@post(`FileUpload/finishChunkApi/{upload}/{model}/{type}`)@response(200, {description: 'FileUpload model instance',content: { 'application/json': { schema: getModelSchemaRef(FileUpload) } },})async finishChunkApi(@param.path.string('upload') upload: string,@param.path.string('model') model: string,@param.path.string('type') type: string,@requestBody() pl: any,): Promise<any> {let uploadId = pl.uploadId;let key = pl.key;let partList = pl.partList;let pathurl = `/${upload}/${model}/${type}/${key}`;const m_dir = `./public/${upload}/${model}/${type}/${uploadId}`;const filePath = `./public/${upload}/${model}/${type}/${key}`;// console.log(uploadId, key, partList, pathurl, " asdasd")let self = this;let size = 0;function mergeFile(dirPath: string, filePath: string, partList: any) {let total = partList.length;return new Promise((resolve, reject) => {fs.readdir(dirPath, (err: any, files: any) => {if (err) {return reject(err);}if (files.length !== total || !files.length) {return reject('上传失败,切片数量不符');}function merge(i: number) {// 合并完成if (i === files.length) {fs.rmdir(dirPath, (err: any) => {console.log(err, 'rmdir');});let date = new Date();let m = {originalname: pl.filename,path: pathurl,timestamp: date,size: size,};return resolve({status: 0,data: {date: date,value: pathurl,url: pathurl,},});}let chunkpath = `${dirPath}/${key}-${i + 1}`;// console.log(chunkpath, 'chunkpath');fs.readFile(chunkpath, 'binary', (err: any, data: any) => {// console.log(data.length);size += data.length;let eTag = SparkMD5.hashBinary(data);if (_.find(partList, { partNumber: i + 1 }).eTag !== eTag) {return reject('上传失败,切片内容不符');}// 将切片追加到存储文件fs.appendFile(filePath, data, { encoding: 'binary' }, () => {// 删除切片文件fs.unlink(chunkpath, () => {// 递归合并merge(i + 1);});});});}merge(0);});});}try {return await mergeFile(m_dir, filePath, partList);} catch (err) {fs.rmdir(m_dir, { recursive: true }, (err: any) => {console.log(err);}); //出错后重新上传return {status: -1,msg: err,};}}
}

file-upload.sevice.ts:

import {BindingScope,config,ContextTags,injectable,Provider,
} from '@loopback/core';
import multer from 'multer';
import {FILE_UPLOAD_SERVICE} from '../keys';
import {FileUploadHandler} from '../types';/*** A provider to return an `Express` request handler from `multer` middleware*/
@injectable({scope: BindingScope.TRANSIENT,tags: {[ContextTags.KEY]: FILE_UPLOAD_SERVICE},
})
export class FileUploadProvider implements Provider<FileUploadHandler> {constructor(@config() private options: multer.Options = {}) {if (!this.options.storage) {// Default to in-memory storagethis.options.storage = multer.memoryStorage();}}value(): FileUploadHandler {return multer(this.options).any();}
}

application.ts:

import { BootMixin } from '@loopback/boot';
import { ApplicationConfig } from '@loopback/core';
import { RepositoryMixin } from '@loopback/repository';
import { RestApplication, RestBindings } from '@loopback/rest';
import { ServiceMixin } from '@loopback/service-proxy';
import multer from 'multer';
import path from 'path';
import {  FILE_UPLOAD_SERVICE,  STORAGE_DIRECTORY } from './keys';export class LbSmartApplication extends BootMixin(ServiceMixin(RepositoryMixin(RestApplication)),
) {constructor(options: ApplicationConfig = {}) {super(options);//...省略this.configureFileUpload(options.fileStorageDirectory);};/*** Configure `multer` options for file upload*/protected configureFileUpload(destination?: string) {// Upload files to `dist/.sandbox` by defaultdestination = destination ?? path.join(__dirname, '../.sandbox');             this.bind(STORAGE_DIRECTORY).to(destination);const multerOptions: multer.Options = {storage: multer.diskStorage({destination,// Use the original file name as isfilename: (req, file, cb) => {file.originalname = Buffer.from(file.originalname, "latin1").toString( "utf8");let originalname = file.originalname;if (req.body && req.body.key && req.body.partNumber) {originalname = `${req.body.key}-${req.body.partNumber}`;}cb(null, originalname);},}),};// Configure the file upload service with multer optionsthis.configure(FILE_UPLOAD_SERVICE).to(multerOptions);}
}

额外:加密算法介绍

在信息安全领域,经常会用到MD5、SHA1、SHA256算法。这三种算法都属于散列算法,或者叫作哈希算法。它们具有输入任意长度,输出长度固定,以及单向性(无法根据散列值还原出消息)的特点。

关于MD5

MD5是一个安全散列算法,输入两个不同的明文不会得到相同的输出值,根据输出值,不能得到原始的明文,即其过程是不可逆的。所以要解密MD5没有现成的算法,只能穷举法,把可能出现的明文,用MD5算法散列之后,把得到的散列值和原始的数据形成一个一对一的映射表,通过匹配从映射表中找出破解密码所对应的原始明文。

关于SHA1

SHA1是一种密码散列函数,可以生成一个被称为消息摘要的160位(20字节)散列值,散列值通常的呈现形式为40个十六进制数。该算法输入报文的长度不限,产生的输出是一个160位的报文摘要。输入是按512 位的分组进行处理的。SHA-1是不可逆的、防冲突,并具有良好的雪崩效应。

关于SHA256

sha256是一种密码散列函数,也可以说是哈希函数。对于任意长度的消息,SHA256都会产生一个256bit长度的散列值,称为消息摘要,可以用一个长度为64的十六进制字符串表示。sha256是SHA-2下细分出的一种算法。SHA-2下又可再分为六个不同的算法标准,包括了:SHA-224、SHA-256、SHA-384、SHA-512、SHA-512/224、SHA-512/256。

关于RSA

是典型的非对称加密算法(对称加密算法又称传统加密算法。 加密和解密使用同一个密钥),主要具有加密解密、数字签名和加签验签的功能。

加密解密:私钥解密,公钥加密。  数字签名-俗称加签验签:私钥加签,公钥验签。 

MD5、SHA1、SHA256有哪些区别?

相同点:

都是密码散列函数,加密不可逆;

都可以实现对任何长度对象加密,都不能防止碰撞;

不同点:

1、校验值的长度不同,MD5校验位的长度是16个字节(128位);SHA1是20个字节(160位);SHA256是32个字节(256位)。

2、运行速度不同,SHA256的运行速度最慢,然后是SHA1,最后是MD5。

MD5、SHA1、SHA256安全性如何?

  在安全性方面,SHA256的安全性最高,然后是SHA1,最后是MD5。虽然SHA256的安全性比较高,但是耗时要比其他两种多很多。

md5、SHA1、SHA256不能解密吗?

  SHA256是目前比较流行的计算机算法之一,相对md5和SHA1而言,SHA256很安全。SHA256是牢不可破的函数,它的256位密钥从未被泄露过。而MD5就不一样了,单纯使用比较容易遭到撞库攻击。通过预先计算知道MD5的对应关系,存在数据库中,然后使用的时候反查,MD5就可能被解密。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/335521.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Visual Studio 的使用

目录 1. 引言 2. 安装和配置 2.1 系统要求 2.2 安装步骤 2.3 初次配置 3. 界面介绍 3.1 菜单栏和工具栏 3.2 解决方案资源管理器 3.3 编辑器窗口 3.4 输出窗口 3.5 错误列表 3.6 属性窗口 4. 项目管理 4.1 创建新项目 4.2 导入现有项目 4.3 项目属性配置 5. 代…

【UE5.1 角色练习】08-物体抬升、抛出技能 - part2

目录 前言 效果 步骤 一、让物体缓慢的飞向手掌 二、向着鼠标方向发射物体 前言 在上一篇&#xff08;【UE5.1 角色练习】08-物体抬升、抛出技能 - part1&#xff09;的基础上继续完成角色将物体吸向手掌&#xff0c;然后通过鼠标点击的方向来发射物体的功能。 效果 步骤…

Nginx配置及优化

Nginx配置及优化 前言nginx.conf拆分理解上线 最近在配置Nginx的时候&#xff0c;偶尔一些细致的理论有些模糊&#xff0c;配置起来费了点功夫&#xff0c;今天来详细写一下我个人的理解&#xff0c;文章参考了一些官网和其他优秀博主的文章http://t.csdnimg.cn/GbID9。 前言 …

【MATLAB源码-第217期】基于matlab的16QAM系统相位偏移估计HOS算法仿真,对比补偿前后的星座图误码率。

操作环境&#xff1a; MATLAB 2022a 1、算法描述 高阶统计量&#xff08;HOS&#xff09;频偏估计算法 高阶统计量&#xff08;Higher Order Statistics, HOS&#xff09;频偏估计算法是一种先进的信号处理技术&#xff0c;广泛应用于现代数字通信系统中&#xff0c;以应对…

【Linux】Linux环境基础开发工具_2

文章目录 四、Linux环境基础开发工具2. vimvim的常见模式 未完待续 四、Linux环境基础开发工具 2. vim vim 是Linux下的一款 多模式编辑器 &#xff0c;可以用来写代码&#xff0c;是 vi 的升级版。 此时无法输入&#xff0c;需要切换模式。 如上图&#xff0c;i 就是切换成…

ch3运输层--计算机网络期末复习(持续更新中)

运输层位于网络层之上 运输层协议提供的某些服务受到网络层协议的限制。比如,时限和带宽保证。 运输层也提供自己的特殊服务。比如,可靠数据传输服务,安全性服务。 网络层:两个主机之间的逻辑通信 运输层:两个进程之间的逻辑通信 网络地址:主机的标识(IP地址) 传输地址: …

【Rust日报】Rust 中的形式验证

文章 - 未来的愿景&#xff1a;Rust 中的形式验证 这篇文章回顾了形式化验证的基本概念&#xff0c;作者展示了如何使用 Hoare triples 来描述和推理程序的正确性&#xff0c;以及如何使用分离逻辑来解决验证的复杂性。文章还解释了为什么 Rust 适用于形式化验证&#xff0c;以…

100个投资者99个选择使用这款EA,WeTrade发现1个事实

为什么100个投资者会有99个选择使用这款EA&#xff0c;是因为这款EA能提供两个版本吗?是因为能控制风险吗?都不是&#xff0c;WeTrade发现1个事实才是这么多投资者选择的原因&#xff0c;那就是能实现100%的盈利率。 我们都知道外汇狙击手EA提供两种版本&#xff0c;分别是标…

OpenAI新模型开始训练!GPT6?

国内可用潘多拉镜像站GPT-4o、GPT-4&#xff08;更多信息请加Q群865143845&#xff09;: 站点&#xff1a;https://xgpt4.ai0.cn/ OpenAI 官网 28 日发文称&#xff0c;新模型已经开始训练&#xff01; 一、新模型开始训练 原话&#xff1a;OpenAI has recently begun training…

【C++】模拟实现string类

&#x1f984;个人主页:修修修也 &#x1f38f;所属专栏:C ⚙️操作环境:Visual Studio 2022 目录 一.了解项目功能 二.逐步实现项目功能模块及其逻辑详解 &#x1f38f;构建成员变量 &#x1f38f;实现string类默认成员函数 &#x1f4cc;构造函数 &#x1f4cc;析构函数…

Spring框架温习

Spring Spring是一个全面的、企业应用开发一站式的解决方案&#xff0c;贯穿表现层、业务层、持久层。但是 Spring仍然可以和其他的框架无缝整合。 Spring 特点&#xff1a; 轻量级、控制反转、面向切面、容器、框架集合 Spring 核心组件&#xff1a; Spring 常用模块&…

【UE 反射】反射的原理是什么?如何使用机制?

目录 0 拓展0.1 静态类型检查0.1.1 静态类型检查的主要原理0.1.2 编译器的工作流程0.1.3 静态类型检查的优点和缺点0.1.4 示例0.1.5 C也可以在运行时类型检查RTTI基本原理RTTI的实现RTTI的工作流程RTTI的限制 0.2 运行时动态类型检查0.2.1 主要特点0.2.2 动态类型检查的实现0.2…

Three.js 入门介绍与环境搭建

Three.js 入门介绍与环境搭建 一、引言 Three.js 是一个强大的用于在网页上创建和展示 3D 图形的 JavaScript 库。艾斯视觉作为ui设计和前端开发服务商在这里很高兴能与你共同探讨学习&#xff1a;它使得开发者能够轻松地构建令人惊叹的 3D 场景和交互体验。在这篇文章中&…

刷代码随想录有感(83):贪心算法——最大子数组和

题干&#xff1a; 代码&#xff1a; class Solution { public:int maxSubArray(vector<int>& nums) {int res INT_MIN;int count 0;for(int i 0; i < nums.size(); i){count nums[i];if(count > res) res count;if(count < 0)count 0;}return res;} …

【微服务】springboot 构建docker镜像多模式使用详解

目录 一、前言 二、微服务常用的镜像构建方案 3.1 使用Dockerfile 3.2 使用docker plugin插件 3.3 使用docker compose 编排文件 三、环境准备 3.1 服务器 3.2 安装JDK环境 3.2.1 创建目录 3.2.2 下载安装包 3.2.3 配置环境变量 2.2.4 查看java版本 3.3 安装maven …

百度页面奔跑的白熊html、css

一、相关知识-动画 1.基本使用&#xff1a;先定义再调用 2. 调用动画 用keyframes定义动画&#xff08;类似定义类选择器&#xff09; keyframes动画名称{ 0%{ width:100px&#xff1b; } 100%{ width:200px; } } 使用动画 div { width:200px; height:200px; background-…

用户流失分析:如何使用Python训练一个用户流失预测模型?

引言 在当今商业环境中&#xff0c;客户流失分析是至关重要的一环。随着市场竞争的加剧&#xff0c;企业需要更加注重保持现有客户&#xff0c;并深入了解他们的离开原因。本文探讨了用户流失分析的核心概念以及如何构建客户流失预测模型的案例。通过分析用户行为数据和交易模式…

【传知代码】自监督高效图像去噪(论文复现)

前言&#xff1a;在数字化时代&#xff0c;图像已成为我们生活、工作和学习的重要组成部分。然而&#xff0c;随着图像获取方式的多样化&#xff0c;图像质量问题也逐渐凸显出来。噪声&#xff0c;作为影响图像质量的关键因素之一&#xff0c;不仅会降低图像的视觉效果&#xf…

牛客NC367 第K个n的排列【困难 dfs,全排列问题 Java/Go/PHP/C++】

题目 题目链接&#xff1a; https://www.nowcoder.com/practice/1595969179464e4c940a90b36abb3c54 思路 全排列问题本文提供的答案在力扣同一道题60. 排列序列&#xff0c;超时了但是截止文章发表日&#xff0c;牛客上是能通过全部测试用例的Java代码 import java.util.*;pu…

【漏洞复现】大华智能物联综合管理平台 fastjson远程代码执行漏洞

0x01 产品简介 大华ICC智能物联综合管理平台对技术组件进行模块化和松耦合&#xff0c;将解决方案分层分级&#xff0c;提高面向智慧物联的数据接入与生态合作能力。 0x02 漏洞概述 由于大华智能物联综合管理平台使用了存在漏洞的Fastson组件,未经身份验让的攻击者可利用 /e…