-
Notifications
You must be signed in to change notification settings - Fork 80
/
Copy pathserver.js
160 lines (140 loc) · 4.83 KB
/
server.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
const express = require('express');
const { S3Client, GetObjectCommand, PutObjectCommand, ListObjectsCommand, HeadObjectCommand } = require('@aws-sdk/client-s3');
const sharp = require('sharp');
const dotenv = require('dotenv');
const path = require('path');
const exifParser = require('exif-parser');
dotenv.config();
const app = express();
const port = process.env.PORT || 3000;
const s3Client = new S3Client({
region: process.env.R2_REGION,
endpoint: process.env.R2_ENDPOINT,
credentials: {
accessKeyId: process.env.R2_ACCESS_KEY_ID,
secretAccessKey: process.env.R2_SECRET_ACCESS_KEY,
},
tls: true,
});
const BUCKET_NAME = process.env.R2_BUCKET_NAME;
const IMAGE_BASE_URL = process.env.R2_IMAGE_BASE_URL;
const IMAGE_DIR = process.env.R2_IMAGE_DIR;
const IMAGE_COMPRESSION_QUALITY = parseInt(process.env.IMAGE_COMPRESSION_QUALITY, 10);
const validImageExtensions = ['.jpg', '.jpeg', '.png', '.gif'];
async function getExifData(key) {
const getObjectParams = {
Bucket: BUCKET_NAME,
Key: key,
};
const imageBuffer = await s3Client.send(new GetObjectCommand(getObjectParams)).then(response => {
return new Promise((resolve, reject) => {
const chunks = [];
response.Body.on('data', (chunk) => chunks.push(chunk));
response.Body.on('end', () => resolve(Buffer.concat(chunks)));
response.Body.on('error', reject);
});
});
const parser = exifParser.create(imageBuffer);
const exifData = parser.parse().tags;
return {
FNumber: exifData.FNumber,
ExposureTime: exifData.ExposureTime,
ISO: exifData.ISO,
};
}
app.use(express.static('public'));
app.get('/images', async (req, res) => {
try {
const images = await s3Client.send(new ListObjectsCommand({
Bucket: BUCKET_NAME,
Prefix: IMAGE_DIR
}));
// 按文件夹分类图片
const imageMap = new Map();
images.Contents
.filter(item => {
const itemExtension = path.extname(item.Key).toLowerCase();
return validImageExtensions.includes(itemExtension);
})
.forEach(item => {
const parts = item.Key.split('/');
// 忽略 preview 文件夹
if (parts.includes('preview')) return;
// 获取文件夹名,如果没有文件夹则归类为 'all'
const folder = parts.length > 2 ? parts[1] : 'all';
if (!imageMap.has(folder)) {
imageMap.set(folder, []);
}
imageMap.get(folder).push({
original: `${IMAGE_BASE_URL}/${item.Key}`,
thumbnail: `${IMAGE_BASE_URL}/${IMAGE_DIR}/preview/${path.basename(item.Key)}`
});
});
// 将分类结果转换为对象
const result = {};
for (const [folder, images] of imageMap.entries()) {
result[folder] = images;
}
res.json(result);
} catch (error) {
console.error('Error loading images:', error);
res.status(500).send('Error loading images');
}
});
app.get('/thumbnail/:key', async (req, res) => {
const key = decodeURIComponent(req.params.key);
const thumbnailKey = `${IMAGE_DIR}/preview/${path.basename(key)}`;
try {
// 检查缩略图是否存在
await s3Client.send(new HeadObjectCommand({
Bucket: BUCKET_NAME,
Key: thumbnailKey
}));
res.redirect(`${IMAGE_BASE_URL}/${thumbnailKey}`);
} catch (error) {
if (error.name === 'NotFound') {
// 如果不存在,生成缩略图
const imageBuffer = await s3Client.send(new GetObjectCommand({
Bucket: BUCKET_NAME,
Key: key
})).then(response => {
return new Promise((resolve, reject) => {
const chunks = [];
response.Body.on('data', (chunk) => chunks.push(chunk));
response.Body.on('end', () => resolve(Buffer.concat(chunks)));
response.Body.on('error', reject);
});
});
const sharpInstance = sharp(imageBuffer).resize(200).withMetadata();
if (IMAGE_COMPRESSION_QUALITY >= 0 && IMAGE_COMPRESSION_QUALITY <= 100) {
sharpInstance.jpeg({ quality: IMAGE_COMPRESSION_QUALITY });
}
const thumbnailBuffer = await sharpInstance.toBuffer();
await s3Client.send(new PutObjectCommand({
Bucket: BUCKET_NAME,
Key: thumbnailKey,
Body: thumbnailBuffer,
ContentType: 'image/jpeg',
}));
res.redirect(`${IMAGE_BASE_URL}/${thumbnailKey}`);
} else {
throw error;
}
}
});
app.get('/exif/:key', async (req, res) => {
const key = decodeURIComponent(req.params.key);
try {
const exifData = await getExifData(key);
res.json(exifData);
} catch (error) {
console.error('Error getting EXIF data:', error);
res.status(500).send('Error getting EXIF data');
}
});
app.get('/config', (req, res) => {
res.json({ IMAGE_BASE_URL: process.env.R2_IMAGE_BASE_URL });
});
app.listen(port, () => {
console.log(`Server is running on http://localhost:${port}`);
});