Skip to content

Commit a3f703f

Browse files
authored
Merge pull request #19 from minorcell/djh
Replace Qiniu cloud storage with local file uploads and static file s…
2 parents 7cc3cdc + a6eacc0 commit a3f703f

6 files changed

Lines changed: 83 additions & 108 deletions

application.example.yml

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,6 @@
11
server:
22
PORT: REPLACEYOURPORT
33

4-
kodo:
5-
ACCESS_KEY: REPLACEYOURACCESSKEY
6-
SECRET_KEY: REPLACEYOURSECRETKEY
7-
BUCKET: REPLACEYOURBUCKET
8-
BASE_URL: REPLACEYOURBASEURL
9-
104
database:
115
type: mysql
126
host: REPLACEYOURHOST

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,12 @@
2121
"@nestjs/jwt": "^11.0.0",
2222
"@nestjs/mapped-types": "*",
2323
"@nestjs/platform-express": "^11.0.1",
24+
"@nestjs/serve-static": "^5.0.3",
2425
"@nestjs/typeorm": "^11.0.0",
2526
"@types/multer": "^1.4.12",
2627
"class-transformer": "^0.5.1",
2728
"class-validator": "^0.14.1",
2829
"mysql2": "^3.13.0",
29-
"qiniu": "^7.14.0",
3030
"reflect-metadata": "^0.2.2",
3131
"rxjs": "^7.8.1",
3232
"typeorm": "^0.3.21",

pnpm-lock.yaml

Lines changed: 38 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/upload/upload.module.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,19 @@
11
import { Module } from '@nestjs/common';
2-
import { TypeOrmModule } from '@nestjs/typeorm';
3-
import { UploadController } from './upload.controller';
42
import { UploadService } from './upload.service';
3+
import { UploadController } from './upload.controller';
4+
import { TypeOrmModule } from '@nestjs/typeorm';
55
import { Upload } from './entities/upload.entity';
6+
import { ServeStaticModule } from '@nestjs/serve-static';
7+
import { join } from 'path';
68

79
@Module({
8-
imports: [TypeOrmModule.forFeature([Upload])],
10+
imports: [
11+
TypeOrmModule.forFeature([Upload]),
12+
ServeStaticModule.forRoot({
13+
rootPath: join(process.cwd(), 'uploads'),
14+
serveRoot: '/uploads',
15+
}),
16+
],
917
controllers: [UploadController],
1018
providers: [UploadService],
1119
})

src/upload/upload.service.ts

Lines changed: 33 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -2,121 +2,56 @@ import { Injectable } from '@nestjs/common';
22
import { ConfigService } from '@nestjs/config';
33
import { InjectRepository } from '@nestjs/typeorm';
44
import { Repository } from 'typeorm';
5-
import * as qiniu from 'qiniu';
65
import { Upload } from './entities/upload.entity';
7-
8-
interface QiniuResponse {
9-
hash: string;
10-
key: string;
11-
}
12-
13-
interface QiniuCallbackInfo {
14-
statusCode: number;
15-
}
6+
import * as fs from 'fs';
7+
import * as path from 'path';
8+
import * as crypto from 'crypto';
169

1710
@Injectable()
1811
export class UploadService {
19-
private mac: qiniu.auth.digest.Mac;
20-
private config: qiniu.conf.Config;
21-
private bucketManager: qiniu.rs.BucketManager;
22-
private uploadToken: string;
12+
private uploadDir: string;
13+
private baseUrl: string;
2314

2415
constructor(
2516
private configService: ConfigService,
2617
@InjectRepository(Upload)
2718
private uploadRepository: Repository<Upload>,
2819
) {
29-
// 初始化七牛云配置
30-
this.mac = new qiniu.auth.digest.Mac(
31-
this.configService.get('kodo.ACCESS_KEY'),
32-
this.configService.get('kodo.SECRET_KEY'),
33-
);
34-
this.config = new qiniu.conf.Config();
35-
// 设置存储区域
36-
this.config.zone = qiniu.zone.Zone_z0; // 根据你的空间所在的区域选择:华东(z0)、华北(z1)、华南(z2)、北美(na0)等
37-
this.bucketManager = new qiniu.rs.BucketManager(this.mac, this.config);
38-
const putPolicy = new qiniu.rs.PutPolicy({
39-
scope: this.configService.get('kodo.BUCKET'),
40-
});
41-
this.uploadToken = putPolicy.uploadToken(this.mac);
20+
this.uploadDir = path.join(process.cwd(), 'uploads');
21+
22+
// 确保上传目录存在
23+
if (!fs.existsSync(this.uploadDir)) {
24+
fs.mkdirSync(this.uploadDir, { recursive: true });
25+
}
4226
}
4327

4428
async uploadFile(file: Express.Multer.File): Promise<Upload> {
45-
const formUploader = new qiniu.form_up.FormUploader(this.config);
46-
const putExtra = new qiniu.form_up.PutExtra();
47-
48-
// 验证配置是否存在
49-
const accessKey = this.configService.get<string>('kodo.ACCESS_KEY');
50-
const secretKey = this.configService.get<string>('kodo.SECRET_KEY');
51-
const bucket = this.configService.get<string>('kodo.BUCKET');
52-
const baseUrl = this.configService.get<string>('kodo.BASE_URL');
29+
try {
30+
// 生成文件hash
31+
const hash = crypto.createHash('sha256')
32+
.update(file.buffer)
33+
.digest('hex');
5334

54-
if (!accessKey || !secretKey || !bucket || !baseUrl) {
55-
throw new Error(
56-
'Missing required Qiniu configuration. Please check your configuration file.',
57-
);
58-
}
35+
// 生成文件名(使用原始文件扩展名)
36+
const ext = path.extname(file.originalname);
37+
const key = `${hash}${ext}`;
38+
const filePath = path.join(this.uploadDir, key);
5939

60-
return new Promise<Upload>((resolve, reject) => {
61-
formUploader.put(
62-
this.uploadToken,
63-
null, // 使用七牛云生成的文件名
64-
file.buffer,
65-
putExtra,
66-
(
67-
err: Error | undefined,
68-
body: QiniuResponse,
69-
info: QiniuCallbackInfo,
70-
) => {
71-
try {
72-
// 检查是否有错误
73-
if (err) {
74-
console.error('Upload error:', err);
75-
return reject(
76-
new Error('File upload failed due to an internal error.'),
77-
);
78-
}
40+
// 保存文件
41+
await fs.promises.writeFile(filePath, file.buffer);
7942

80-
// 检查 HTTP 状态码是否为成功
81-
if (info.statusCode !== 200) {
82-
console.error('Upload failed with status code:', info.statusCode);
83-
return reject(
84-
new Error(
85-
`File upload failed with status code: ${info.statusCode}`,
86-
),
87-
);
88-
}
89-
// 获取基础 URL 并构造上传对象
90-
const baseUrl = this.configService.get<string>('kodo.BASE_URL');
91-
const upload = new Upload();
92-
upload.hash = body.hash;
93-
upload.key = body.key;
94-
upload.url = `${baseUrl}/${body.key}`;
43+
// 创建上传记录
44+
const upload = new Upload();
45+
upload.hash = hash;
46+
upload.key = key;
47+
upload.url = `/uploads/${key}`;
9548

96-
// 将上传信息保存到数据库
97-
try {
98-
this.uploadRepository
99-
.save(upload)
100-
.then((savedUpload) => resolve(savedUpload))
101-
.catch((error) =>
102-
reject(new Error(`Failed to save upload: ${error.message}`)),
103-
); // 成功时返回保存的对象
104-
} catch (dbError) {
105-
console.error('Database save error:', dbError);
106-
reject(
107-
new Error('Failed to save upload information to the database.'),
108-
);
109-
}
110-
} catch (unexpectedError) {
111-
// 捕获任何意外错误
112-
console.error('Unexpected error during upload:', unexpectedError);
113-
reject(
114-
new Error('An unexpected error occurred during file upload.'),
115-
);
116-
}
117-
},
118-
);
119-
});
49+
// 保存到数据库
50+
return await this.uploadRepository.save(upload);
51+
} catch (error) {
52+
console.error('Upload error:', error);
53+
throw new Error('File upload failed: ' + error.message);
54+
}
12055
}
12156

12257
async getFiles(page: number = 1, limit: number = 10) {
408 KB
Loading

0 commit comments

Comments
 (0)