返回 AiToEarn
oss.service.ts
根目录 / project / aitoearn-electron / server / src / lib / oss / oss.service.ts
1 /*
2 * @Author: nevin
3 * @Date: 2022-03-03 16:59:23
4 * @LastEditors: nevin
5 * @LastEditTime: 2025-02-25 23:11:01
6 * @Description: oss函数
7 */
8 import { Injectable, Inject, HttpStatus } from '@nestjs/common';
9 import { ConfigService } from '@nestjs/config';
10 import OSS from 'ali-oss';
11 import * as moment from 'moment';
12 import { Duplex } from 'stream';
13 import { v4 as uuidv4 } from 'uuid';
14 import { OssNewFilePath } from './oss.interface';
15 import axios from 'axios';
16 @Injectable()
17 export class OssService {
18 private HOST_URL: string;
19 private NODE_ENV: string;
20 constructor(
21 @Inject('OSS_CLIENT_PROVIDER') private client: OSS,
22 private configService: ConfigService,
23 ) {
24 this.HOST_URL = this.configService.get('OSS_CONFIG.HOST_URL');
25 this.NODE_ENV = this.configService.get('SERVER_CONFIG.NODE_ENV');
26 }
27
28 private getNewFilePath(opt: OssNewFilePath) {
29 let { path, newName } = opt;
30
31 path = `${this.NODE_ENV}/${opt.permanent ? '' : 'temp/'}${path || `nopath/${moment().format('YYYYMM')}`}`;
32 path = path.replace('//', '/');
33 newName = newName || uuidv4();
34
35 return {
36 path,
37 newName,
38 };
39 }
40
41 /**
42 * 获取完整url中的文件名即去除host域名
43 * @param url
44 * @returns
45 */
46 getFileNameFromUrl(url: string) {
47 const tempStr = url.split(`${this.HOST_URL}/`);
48 return tempStr.length <= 0 ? '' : tempStr[tempStr.length - 1];
49 }
50
51 /**
52 * 文件上传
53 * @param {Express.Multer.File} file 文件buffer流对象
54 * @param {string | undefined} path 路径,不传就会使用‘nopath’前缀
55 * @param {string | undefined} newName 新的文件名
56 * @param {string | undefined} permanent 是否为永久目录,默认临时
57 * @returns
58 */
59 async upFileStream(
60 file: Express.Multer.File,
61 path?: string,
62 newName?: string,
63 permanent?: boolean,
64 ) {
65 const { buffer, mimetype } = file;
66 const ret = this.getNewFilePath({ path, newName, permanent });
67 path = ret.path;
68 newName = ret.newName;
69
70 const tempStr = mimetype.split('/');
71 const fileTypeStr = tempStr[tempStr.length - 1];
72
73 const stream = new Duplex();
74 stream.push(buffer);
75 stream.push(null);
76
77 try {
78 const upRes = await this.client.putStream(
79 `${path}/${newName}.${fileTypeStr}`,
80 stream,
81 );
82
83 const {
84 name,
85 res: { status },
86 } = upRes;
87
88 if (status !== HttpStatus.OK) throw new Error('文件上传失败');
89
90 return {
91 name,
92 };
93 } catch (error) {
94 throw error;
95 }
96 }
97
98 /**
99 * 去除文件前置
100 * @param filePath
101 */
102 private noHostFilePath(filePath: string) {
103 const hostUrl = this.configService.get('OSS_CONFIG.HOST_URL');
104
105 const _hostUrl = hostUrl.replace('https', 'http');
106 if (filePath.indexOf(_hostUrl) === 0) {
107 filePath = filePath.replace(`${_hostUrl}/`, '');
108 return this.noHostFilePath(filePath);
109 }
110
111 return filePath;
112 }
113
114 /**
115 * 将临时目录文件转到新的目录文件
116 * @param filePath 原文件地址
117 * @param newFilePath 不填就是去除temp标识
118 * @returns
119 */
120 async changeFilePath(
121 filePath: string,
122 newFilePath?: string,
123 ): Promise<string> {
124 // 干掉前置
125 filePath = this.noHostFilePath(filePath);
126
127 // 复制文件
128 newFilePath = newFilePath || filePath.replace('temp/', '');
129
130 if (filePath === newFilePath) return filePath;
131
132 try {
133 const {
134 res: { status },
135 } = await this.client.copy(newFilePath, filePath);
136 if (status !== HttpStatus.OK) throw new Error('文件上传失败');
137 return newFilePath;
138 } catch (error) {
139 console.log('========== error', error);
140 throw error;
141 }
142 }
143
144 /**
145 * 上传二进制流文件
146 * @param buffer 二进制流 base64格式
147 * @param path 路径
148 * @param permanent 是否为永久目录,默认临时
149 */
150 async uploadByStream(
151 buffer: Buffer, // base64格式(不带前缀)
152 option: {
153 path?: string;
154 permanent?: boolean;
155 fileType: string;
156 },
157 ): Promise<string> {
158 const { path, permanent, fileType } = option;
159 const objectName = `${this.NODE_ENV}/${permanent ? '' : 'temp/'}${path || 'nopath'}${`/${moment().format('YYYYMM')}/${uuidv4()}.${fileType}`}`;
160
161 // 上传到阿里云
162 try {
163 const res = await this.client.put(objectName, buffer);
164 return res.name;
165 } catch (error) {
166 console.log('----- uploadByBase64 error -----', error);
167 return '';
168 }
169 }
170
171 /**
172 * 根据网络地址上传到阿里云
173 * @param url
174 * @param option
175 * @returns
176 */
177 async upFileByUrl(
178 url: string,
179 option: {
180 path?: string;
181 permanent?: boolean;
182 },
183 ) {
184 const { path, permanent } = option;
185 let fileType = '';
186 if (url.includes('.')) {
187 fileType = url.split('.').pop();
188 }
189 const objectName = `${this.NODE_ENV}/${permanent ? '' : 'temp/'}${path || 'nopath'}${`/${moment().format('YYYYMM')}/${uuidv4()}.${fileType}`}`;
190
191 try {
192 const response = await axios.get(url, { responseType: 'arraybuffer' });
193 const buffer = Buffer.from(response.data);
194
195 const res = await this.client.put(objectName, buffer);
196 return res.name;
197 } catch (error) {
198 console.log('----- upFileByUrl error -----', error);
199 return '';
200 }
201 }
202
203 /**
204 * TODO: 未完成 未使用 上传base64图片
205 */
206 async uploadByBuffer(
207 base64Img: string,
208 path?: string,
209 permanent?: boolean,
210 ): Promise<string> {
211 const imageBuffer = Buffer.from(base64Img, 'base64');
212 const objectName = `${this.NODE_ENV}/${permanent ? '' : 'temp/'}${`nopath/${moment().format('YYYYMM')}/${uuidv4()}.png`}`;
213 try {
214 const {
215 url,
216 res: { status },
217 } = await this.client.put(objectName, imageBuffer);
218
219 if (status === 200) {
220 return url;
221 } else {
222 return '';
223 }
224 // eslint-disable-next-line @typescript-eslint/no-unused-vars
225 } catch (error) {
226 return '';
227 }
228 }
229 }
230
230 lines TYPESCRIPT