当前位置: 首页 > ds >正文

nestjs 阿里云服务端签名

1、将配置文件中的信息更换成自己的即可使用  服务端

下载包

npm install ali-oss
npm install @alicloud/credentials
import { Injectable } from "@nestjs/common";
import { ResultData } from "src/utils/result-data";
import { STS } from 'ali-oss';
import OSS from 'ali-oss';
import { getCredential } from 'ali-oss/lib/common/signUtils'
import { getStandardRegion } from 'ali-oss/lib/common/utils/getStandardRegion'
import { policy2Str } from 'ali-oss/lib/common/utils/policy2Str'
import { ConfigService } from "@nestjs/config";@Injectable()
export class UploadService {constructor(private readonly configService: ConfigService,) { }async generateSignature() {// 初始化STS客户端let sts = new STS({accessKeyId: this.configService.get('oss.accessKeyId'),accessKeySecret: this.configService.get('oss.accessKeySecret')});// 调用assumeRole接口获取STS临时访问凭证const result = await sts.assumeRole(this.configService.get('oss.assumeRole'), '', '3600');const accessKeyId = result.credentials.AccessKeyId;const accessKeySecret = result.credentials.AccessKeySecret;const securityToken = result.credentials.SecurityToken;const client = new OSS({region: this.configService.get('oss.region'),accessKeyId,accessKeySecret,stsToken: securityToken,bucket: this.configService.get('oss.bucket'),refreshSTSTokenInterval: 0,refreshSTSToken: async () => {const { accessKeyId, accessKeySecret, securityToken } = await client.getCredential();return { accessKeyId, accessKeySecret, stsToken: securityToken };},});const formData = new Map();const date = new Date();const expirationDate = new Date(date);expirationDate.setMinutes(date.getMinutes() + 10);function padTo2Digits(num) {return num.toString().padStart(2, '0');}function formatDateToUTC(date) {return (date.getUTCFullYear() +padTo2Digits(date.getUTCMonth() + 1) +padTo2Digits(date.getUTCDate()) +'T' +padTo2Digits(date.getUTCHours()) +padTo2Digits(date.getUTCMinutes()) +padTo2Digits(date.getUTCSeconds()) +'Z');}const formattedDate = formatDateToUTC(expirationDate);// 生成x-oss-credential并设置表单数据const credential = getCredential(formattedDate.split('T')[0], getStandardRegion(client.options.region), client.options.accessKeyId);formData.set('x_oss_date', formattedDate);formData.set('x_oss_credential', credential);formData.set('x_oss_signature_version', 'OSS4-HMAC-SHA256');// 创建policy// 示例policy表单域只列举必填字段const policy: { expiration: string, conditions: any[] } = {expiration: expirationDate.toISOString(),conditions: [{ 'bucket': this.configService.get('oss.bucket') },{ 'x-oss-credential': credential },{ 'x-oss-signature-version': 'OSS4-HMAC-SHA256' },{ 'x-oss-date': formattedDate },],};// 如果存在STS Token,添加到策略和表单数据中if (client.options.stsToken) {policy.conditions.push({ 'x-oss-security-token': client.options.stsToken });formData.set('security_token', client.options.stsToken);}// 生成签名并设置表单数据const signature = client.signPostObjectPolicyV4(policy, date);formData.set('policy', Buffer.from(policy2Str(policy), 'utf8').toString('base64'));formData.set('signature', signature);// 返回表单数据const data = {host: `http://${client.options.bucket}.oss-${client.options.region}.aliyuncs.com`,policy: Buffer.from(policy2Str(policy), 'utf8').toString('base64'),x_oss_signature_version: 'OSS4-HMAC-SHA256',x_oss_credential: credential,x_oss_date: formattedDate,signature: signature,accessKeyId: client.options.accessKeyId,accessKeySecret: client.options.accessKeySecret,region: client.options.region,bucket: client.options.bucket,dir: this.configService.get('oss.dir'), // 指定上传到OSS的文件前缀stsToken: client.options.stsToken};return ResultData.ok(data)}
}

2、使用        客户端

下载包:

npm i ali-oss

主要代码:

// OSS实例
const getOssClient = (ossInfo: any) => {const client = new OSS({region: ossInfo.region,authorizationV4: true,accessKeyId: ossInfo.accesskeyid,accessKeySecret: ossInfo.accesskeysecret,stsToken: ossInfo.security_token,bucket: ossInfo.bucket,});return client;
};// 普通上传
await ossClient.put(`${dir}/${fileName}`, file)//分包上传
ossClient.multipartUpload(`${dir}/${fileName}`, file, {parallel: 4,partSize: 1024 * 1024 * 10,progress: function (p: any, cpt: any) {console.log(p, cpt);},
}).then(function (res: any) {setFileCount(fileCount - 1);for (let i in res.res.requestUrls) {fileListRef.current.push({url: res.res.requestUrls[i],courseId: '',});}updateFileString?.(JSON.parse(JSON.stringify(fileListRef.current)));
});
import { getOssToken } from '@/services/system';
// @ts-ignore
import OSS from 'ali-oss';
import { Button, Image, message, Spin, Upload } from 'antd';
import { useEffect, useRef } from 'react';
import { useImmer } from 'use-immer';
const UploadFile = ({maxCount = 1,fileList,multiple = true,updateFileString,children,accept = '*',disabled = false,subPackageSize = 1024 * 1024 * 100, //分包大小
}: {maxCount?: number;fileList: { url: string; courseId: string }[];updateFileString?: Function;children?: any;multiple?: boolean;accept?: string;disabled?: boolean;subPackageSize?: number;
}) => {const fileListRef = useRef<Array<{ url: string; courseId: string }>>([]);const [update, setUpdate] = useImmer(false);useEffect(() => {setUpdate(!update);fileListRef.current = JSON.parse(JSON.stringify(fileList));}, [fileList]);const [fileCount, setFileCount] = useImmer(0);useEffect(() => {if (fileCount > 0) {message.loading({content: '上传中...',duration: 0});} else {message.destroy()}}, [fileCount]);function getFileBaseNameAndExtension(file: File) {// 确保传入的是 File 对象或具有 name 属性的对象if (!file || typeof file.name === 'undefined') {throw new Error('无效的文件对象');}const fullName = file.name;const lastDotIndex = fullName.lastIndexOf('.');let fileNameWithoutExt;let extension;if (lastDotIndex === -1) {// 文件名中没有点fileNameWithoutExt = fullName;extension = ''; // 或者可以设为 null} else {// 截取点之前的部分作为文件名fileNameWithoutExt = fullName.substring(0, lastDotIndex);// 截取点及之后的部分作为扩展名(包含点)extension = fullName.substring(lastDotIndex); // 例如 ".jpg", ".pdf"// 如果你希望 extension 不包含点,使用: extension = fullName.substring(lastDotIndex + 1);}return {fileName: fileNameWithoutExt + '_flyco_' + new Date().getTime(), // 不带后缀的文件名extension: extension, // 后缀名(包含点)};}function parseOSSFileName(fileName: string) {fileName = decodeURIComponent(fileName.split('/')[fileName.split('/').length - 1]);// 1. 找到最后一个点的位置来分离扩展名const lastDotIndex = fileName.lastIndexOf('.');// 2. 如果有后缀名if (lastDotIndex !== -1) {const extension = fileName.substring(lastDotIndex); // 包含点,如 ".docx"const nameWithoutExt = fileName.substring(0, lastDotIndex); // 去掉后缀的部分// 3. 使用正则匹配 _flyco_后跟纯数字 的模式const match = nameWithoutExt.match(/^(.+?)_flyco_\d+$/);// 4. 如果匹配成功,返回 捕获的原始名 + 后缀;否则返回原文件名if (match) {return match[1] + extension; // 原始名 + 后缀} else {return match + extension;}}// 5. 如果没有后缀 或 不符合规则,直接返回原文件名return fileName;}// 获取ossApiconst getOssTokenApi = (file: any) => {getOssToken().then(async (res) => {if (res.code == 200) {const OssInit = getOssClient(res.data);if (file.size > subPackageSize) {updateSubPackageFile(res.data.dir, OssInit, file);} else {uploadPackageFile(res.data.dir, OssInit, file);}}});};// OSS实例const getOssClient = (ossInfo: any) => {const client = new OSS({region: ossInfo.region,authorizationV4: true,accessKeyId: ossInfo.accessKeyId,accessKeySecret: ossInfo.accessKeySecret,stsToken: ossInfo.stsToken,bucket: ossInfo.bucket,});return client;};// 分包上传const updateSubPackageFile = (dir: string, ossClient: any, file: any) => {const fileBaseNameAndExtension = getFileBaseNameAndExtension(file);const fileName = fileBaseNameAndExtension.fileName + fileBaseNameAndExtension.extension;ossClient.multipartUpload(`${dir}/${fileName}`, file, {parallel: 4,partSize: 1024 * 1024 * 10,progress: function (p: any, cpt: any) {console.log(p, cpt);},}).then(function (res: any) {setFileCount(fileCount - 1);for (let i in res.res.requestUrls) {fileListRef.current.push({url: res.res.requestUrls[i],courseId: '',});}updateFileString?.(JSON.parse(JSON.stringify(fileListRef.current)));});};// 普通上传const uploadPackageFile = async (dir: string, ossClient: any, file: any) => {const fileBaseNameAndExtension = getFileBaseNameAndExtension(file);const fileName = fileBaseNameAndExtension.fileName + fileBaseNameAndExtension.extension;const result = await ossClient.put(`${dir}/${fileName}`, file);setFileCount(fileCount - 1);fileListRef.current.push({url: result.url,courseId: '',});updateFileString?.(JSON.parse(JSON.stringify(fileListRef.current)));};return (<div className="flex items-center flex-wrap">{fileListRef.current.map((item: { url: string; courseId: string }, index: number) => {const fileName = parseOSSFileName(item.url);if (accept != 'image/*') {return (<div key={index} className="text-mainColor ml-2 mb-2 mr-3 border-b-[2px] border-t-[0]  border-x-[0] border-mainColor border-solid flex-wrap flex max-w-[200px] cursor-pointer"><div className="max-w-[150px] text-ellipsis"><a target="_blank" className="max-w-[150px] text-ellipsis inline-block overflow-hidden text-nowrap" href={item.url}>{fileName}</a></div>{!disabled && (<divclassName="iconfont icon-shanchu ml-2 cursor-pointer"onClick={() => {fileListRef.current.splice(index, 1);updateFileString?.(JSON.parse(JSON.stringify(fileListRef.current)));}}></div>)}</div>);} else {return (<div className="w-[80px] h-[80px] text-center relative ml-2 border-dashed border-[#ccc] border-[1px]"><Imageheight={80}width={'auto'}src={item.url}key={index}fallback="data:image/png;base64,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"/>{!disabled && (<divclassName="iconfont icon-shanchu text-[red] cursor-pointer ml-2 absolute right-0 top-0 cursor-pointer"onClick={() => {fileListRef.current.splice(index, 1);updateFileString?.(JSON.parse(JSON.stringify(fileListRef.current)));}}></div>)}</div>);}})}{fileListRef.current.length < maxCount && !disabled ? (<Uploadaccept={accept ? accept : '*'}fileList={[]}customRequest={(e: any) => {setFileCount(fileCount + 1);getOssTokenApi(e.file);}}multiple={multiple}><div className=" ml-3">{children ? children : <Button type="primary">上传附件</Button>}</div></Upload>) : ('')}</div>);
};export default UploadFile;

http://www.xdnf.cn/news/20041.html

相关文章:

  • 深度学习篇---SGD+Momentum优化器
  • Photoshop - Photoshop 触控手势
  • 电表连网不用跑现场!耐达讯自动化RS485转Profinet网关 远程配置+技术支持,真能做到!
  • ASP.NET 实战:用 SqlCommand 打造一个安全的用户注册功能
  • SIC8833芯片智能充气泵设计方案
  • 原创未发表!POD-PINN本征正交分解结合物理信息神经网络多变量回归预测模型,Matlab实现
  • 第二家公司虽然用PowerBI ,可能更适合用以前的QuickBI
  • pip completion工具作用(生成命令行自动补全脚本)(与pip-bash-completion区别)
  • 东土智建 | 让塔吊更聪明的“四大绝技”工地安全效率双升级
  • EasyMeeting-注册登录
  • PDF-XChange Editor:全功能PDF阅读和编辑软件
  • 《华为基本法》——企业文化的精髓,你学习了几条?
  • 技术实战:从零开发一个淘宝商品实时数据采集接口
  • 《嵌入式硬件(一):裸机概念与80c51单片机基础》
  • Docker 运行 PolarDB-for-PostgreSQL 的命令,并已包含数据持久化配置
  • Scrapy框架实战:大规模爬取华为应用市场应用详情数据
  • 实现 TypeScript 内置工具类型(源码解析与实现)
  • C语言中的运算符
  • 自动化运维-ansible中的条件判断
  • 前端框架(Vue/React):界面更新的运行链路
  • mysy2使用
  • CC攻击的主要来源
  • 鸿蒙Next图形绘制指南:从基础几何图形到复杂UI设计
  • vue3 vite 自适应方案
  • Java+AI开发实战与知识点归纳系列:Spring流式输出实战——LangChain4j与Ollama集成
  • 2025 大数据时代值得考的证书排名前八​
  • TypeScript与JavaScript:从动态少年到稳重青年的成长之路
  • “企业版维基百科”Confluence
  • STM32 - Embedded IDE - GCC - 如何在工程中定义一段 NoInit RAM 内存
  • 爬取m3u8视频完整教程