Skip to content

Commit 862601d

Browse files
committed
0.13.2
fix(s3-filebase): 修复S3认证失败和文件名编码问题
1 parent 71e271f commit 862601d

File tree

2 files changed

+380
-216
lines changed

2 files changed

+380
-216
lines changed

cloudflare-worker-js-api/API_IMG_IPFS-S3filebase.js

Lines changed: 175 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -8,135 +8,217 @@
88
*/
99

1010
async function handles3filebaseRequest(request) {
11-
if (request.method !== 'POST' || !request.headers.get('Content-Type').includes('multipart/form-data')) {
12-
return new Response('Invalid request', { status: 400 });
13-
}
11+
console.log('[S3-Filebase] Starting request handling');
1412

15-
try {
16-
// 从 KV 获取配置
13+
if (request.method !== 'POST' || !request.headers.get('Content-Type').includes('multipart/form-data')) {
14+
console.error('[S3-Filebase] Invalid request method or content type:', {
15+
method: request.method,
16+
contentType: request.headers.get('Content-Type')
17+
});
18+
return new Response('Invalid request', { status: 400 });
19+
}
20+
21+
try {
22+
console.log('[S3-Filebase] Fetching configuration from KV store');
1723
const config = await WORKER_IMGBED.get('s3filebase_config', 'json');
1824
if (!config || !config.accessKey || !config.secretKey || !config.bucket) {
19-
throw new Error('Invalid S3 configuration');
25+
console.error('[S3-Filebase] Invalid configuration:', {
26+
hasConfig: !!config,
27+
hasAccessKey: !!config?.accessKey,
28+
hasSecretKey: !!config?.secretKey,
29+
hasBucket: !!config?.bucket
30+
});
31+
throw new Error('Invalid S3 configuration');
2032
}
21-
33+
console.log('[S3-Filebase] Configuration loaded successfully');
34+
2235
const formData = await request.formData();
2336
const file = formData.get('image');
2437
if (!file) {
25-
return new Response('No file found', { status: 400 });
38+
console.error('[S3-Filebase] No file found in form data');
39+
return new Response('No file found', { status: 400 });
2640
}
27-
28-
const now = new Date();
29-
const timestamp = now.toISOString()
30-
.replace(/[-:]/g, '')
31-
.split('.')[0]
32-
.replace('T', '_');
41+
42+
// 获取安全的文件名
43+
const originalName = file.name;
44+
const extension = originalName.split('.').pop() || '';
45+
const timestamp = new Date().toISOString()
46+
.replace(/[-:]/g, '')
47+
.split('.')[0]
48+
.replace('T', '_');
3349

34-
const fileName = file.name.split('.')[0];
35-
const extension = file.name.split('.').pop() || '';
36-
const s3Key = `${fileName}_${timestamp}.${extension}`;
50+
// 生成安全的文件名:使用时间戳和随机字符串,避免中文和特殊字符
51+
const safeFileName = `${timestamp}_${Math.random().toString(36).substring(2, 15)}.${extension}`;
52+
console.log('[S3-Filebase] File details:', {
53+
originalName,
54+
safeFileName,
55+
type: file.type,
56+
size: file.size
57+
});
58+
3759
const content = await file.arrayBuffer();
38-
60+
console.log('[S3-Filebase] File content loaded:', {
61+
contentSize: content.byteLength
62+
});
63+
64+
// AWS 签名所需的时间戳
65+
const now = new Date();
3966
const amzdate = now.toISOString().replace(/[:-]|\.\d{3}/g, '');
4067
const dateStamp = amzdate.slice(0, 8);
68+
69+
// 计算内容哈希
4170
const contentHash = await crypto.subtle.digest('SHA-256', content)
42-
.then(buf => Array.from(new Uint8Array(buf))
43-
.map(b => b.toString(16).padStart(2, '0'))
44-
.join(''));
45-
46-
const canonicalUri = `/${config.bucket}/${s3Key}`;
71+
.then(buf => Array.from(new Uint8Array(buf))
72+
.map(b => b.toString(16).padStart(2, '0'))
73+
.join(''));
74+
75+
// URI 编码的路径
76+
const encodedKey = encodeURIComponent(safeFileName).replace(/%20/g, '+');
77+
const canonicalUri = `/${config.bucket}/${encodedKey}`;
78+
79+
// 准备签名所需的头部
4780
const uploadHeaders = {
48-
'Host': 's3.filebase.com',
49-
'Content-Type': file.type || 'application/octet-stream',
50-
'X-Amz-Content-SHA256': contentHash,
51-
'X-Amz-Date': amzdate
81+
'Host': 's3.filebase.com',
82+
'Content-Type': file.type || 'application/octet-stream',
83+
'X-Amz-Content-SHA256': contentHash,
84+
'X-Amz-Date': amzdate
5285
};
53-
86+
87+
console.log('[S3-Filebase] Request preparation:', {
88+
canonicalUri,
89+
amzdate,
90+
contentHashPrefix: contentHash.substring(0, 16) + '...'
91+
});
92+
5493
const algorithm = 'AWS4-HMAC-SHA256';
5594
const region = 'us-east-1';
5695
const service = 's3';
5796
const scope = `${dateStamp}/${region}/${service}/aws4_request`;
58-
97+
98+
// 准备规范请求
5999
const canonicalHeaders = Object.entries(uploadHeaders)
60-
.map(([k, v]) => `${k.toLowerCase()}:${v}\n`)
61-
.sort()
62-
.join('');
100+
.map(([k, v]) => `${k.toLowerCase()}:${v}\n`)
101+
.sort()
102+
.join('');
63103
const signedHeaders = Object.keys(uploadHeaders)
64-
.map(k => k.toLowerCase())
65-
.sort()
66-
.join(';');
67-
104+
.map(k => k.toLowerCase())
105+
.sort()
106+
.join(';');
107+
68108
const canonicalRequest = [
69-
'PUT',
70-
canonicalUri,
71-
'',
72-
canonicalHeaders,
73-
signedHeaders,
74-
contentHash
109+
'PUT',
110+
canonicalUri,
111+
'',
112+
canonicalHeaders,
113+
signedHeaders,
114+
contentHash
75115
].join('\n');
76-
116+
117+
console.log('[S3-Filebase] Canonical request prepared:', {
118+
method: 'PUT',
119+
uri: canonicalUri,
120+
signedHeaders
121+
});
122+
77123
const stringToSign = [
78-
algorithm,
79-
amzdate,
80-
scope,
81-
await crypto.subtle.digest('SHA-256', new TextEncoder().encode(canonicalRequest))
82-
.then(buf => Array.from(new Uint8Array(buf))
83-
.map(b => b.toString(16).padStart(2, '0'))
84-
.join(''))
124+
algorithm,
125+
amzdate,
126+
scope,
127+
await crypto.subtle.digest('SHA-256', new TextEncoder().encode(canonicalRequest))
128+
.then(buf => Array.from(new Uint8Array(buf))
129+
.map(b => b.toString(16).padStart(2, '0'))
130+
.join(''))
85131
].join('\n');
86-
132+
133+
// 生成签名密钥
87134
let key = await crypto.subtle.importKey(
88-
'raw',
89-
new TextEncoder().encode(`AWS4${config.secretKey}`),
90-
{ name: 'HMAC', hash: 'SHA-256' },
91-
false,
92-
['sign']
93-
);
94-
95-
for (const msg of [dateStamp, region, service, 'aws4_request']) {
96-
key = await crypto.subtle.importKey(
97135
'raw',
98-
await crypto.subtle.sign('HMAC', key, new TextEncoder().encode(msg)),
136+
new TextEncoder().encode(`AWS4${config.secretKey}`),
99137
{ name: 'HMAC', hash: 'SHA-256' },
100138
false,
101139
['sign']
102-
);
140+
);
141+
142+
for (const msg of [dateStamp, region, service, 'aws4_request']) {
143+
key = await crypto.subtle.importKey(
144+
'raw',
145+
await crypto.subtle.sign('HMAC', key, new TextEncoder().encode(msg)),
146+
{ name: 'HMAC', hash: 'SHA-256' },
147+
false,
148+
['sign']
149+
);
103150
}
104-
151+
152+
// 计算最终签名
105153
const signature = await crypto.subtle.sign(
106-
'HMAC',
107-
key,
108-
new TextEncoder().encode(stringToSign)
154+
'HMAC',
155+
key,
156+
new TextEncoder().encode(stringToSign)
109157
);
110-
111-
const authorization =
112-
`${algorithm} ` +
113-
`Credential=${config.accessKey}/${scope}, ` +
114-
`SignedHeaders=${signedHeaders}, ` +
115-
`Signature=${Array.from(new Uint8Array(signature))
116-
.map(b => b.toString(16).padStart(2, '0'))
117-
.join('')}`;
118-
119-
const uploadResponse = await fetch(`https://s3.filebase.com${canonicalUri}`, {
120-
method: 'PUT',
121-
headers: {
122-
...uploadHeaders,
123-
'Authorization': authorization
124-
},
125-
body: content
158+
159+
// 构建授权头
160+
const credential = `${config.accessKey}/${dateStamp}/${region}/${service}/aws4_request`;
161+
const authorization = [
162+
`${algorithm} Credential=${credential}`,
163+
`SignedHeaders=${signedHeaders}`,
164+
`Signature=${Array.from(new Uint8Array(signature))
165+
.map(b => b.toString(16).padStart(2, '0'))
166+
.join('')}`
167+
].join(', ');
168+
169+
console.log('[S3-Filebase] Authorization prepared:', {
170+
credentialPrefix: credential.split('/')[0] + '/...',
171+
signedHeadersCount: signedHeaders.split(';').length
126172
});
127-
173+
174+
// 发送上传请求
175+
const uploadUrl = `https://s3.filebase.com${canonicalUri}`;
176+
console.log('[S3-Filebase] Sending upload request to:', uploadUrl);
177+
178+
const uploadResponse = await fetch(uploadUrl, {
179+
method: 'PUT',
180+
headers: {
181+
...uploadHeaders,
182+
'Authorization': authorization
183+
},
184+
body: content
185+
});
186+
128187
if (!uploadResponse.ok) {
129-
throw new Error(`Upload failed with status ${uploadResponse.status}`);
188+
const errorBody = await uploadResponse.text();
189+
console.error('[S3-Filebase] Upload failed:', {
190+
status: uploadResponse.status,
191+
statusText: uploadResponse.statusText,
192+
responseBody: errorBody,
193+
headers: Object.fromEntries([...uploadResponse.headers])
194+
});
195+
throw new Error(`Upload failed with status ${uploadResponse.status}: ${errorBody}`);
130196
}
131-
197+
198+
console.log('[S3-Filebase] Upload successful');
132199
const cid = uploadResponse.headers.get('x-amz-meta-cid');
133200
if (!cid) {
134-
throw new Error('CID not found in response');
201+
console.error('[S3-Filebase] CID not found in response headers:',
202+
Object.fromEntries([...uploadResponse.headers]));
203+
throw new Error('CID not found in response');
135204
}
136-
137-
return new Response(`https://i0.wp.com/i0.img2ipfs.com/ipfs/${cid}`);
138-
139-
} catch (error) {
140-
return new Response(`Upload failed: ${error.message}`, { status: 500 });
141-
}
142-
}
205+
206+
const finalUrl = `https://i0.wp.com/i0.img2ipfs.com/ipfs/${cid}`;
207+
console.log('[S3-Filebase] Generated final URL:', finalUrl);
208+
return new Response(finalUrl);
209+
210+
} catch (error) {
211+
console.error('[S3-Filebase] Error in request handling:', {
212+
name: error.name,
213+
message: error.message,
214+
stack: error.stack
215+
});
216+
return new Response(`Upload failed: ${error.message}`, {
217+
status: 500,
218+
headers: {
219+
'Content-Type': 'text/plain',
220+
'X-Error-Details': error.message
221+
}
222+
});
223+
}
224+
}

0 commit comments

Comments
 (0)