S3FB上机测试
This commit is contained in:
BlueSkyXN 2024-09-20 15:52:59 +08:00
parent 5d7fe7deb8
commit 1052c6f5e1
2 changed files with 56 additions and 410 deletions

View File

@ -2,6 +2,8 @@ addEventListener('fetch', event => {
event.respondWith(handles3filebaseRequest(event.request)); event.respondWith(handles3filebaseRequest(event.request));
}); });
import { AwsClient } from 'aws4fetch';
async function handles3filebaseRequest(request) { async function handles3filebaseRequest(request) {
if (request.method !== 'POST') { if (request.method !== 'POST') {
return new Response('Method not allowed', { status: 405 }); return new Response('Method not allowed', { status: 405 });
@ -23,8 +25,7 @@ async function handles3filebaseRequest(request) {
// 检查是否成功解析 // 检查是否成功解析
if (!bucketName || !accessKeyId || !secretAccessKey) { if (!bucketName || !accessKeyId || !secretAccessKey) {
console.error('Failed to parse S3FILEBASE_KEY environment variable.'); return new Response('Server configuration error: Invalid S3FILEBASE_KEY', { status: 500 });
return new Response('Server configuration error.', { status: 500 });
} }
const region = 'us-east-1'; // Filebase默认使用'us-east-1' const region = 'us-east-1'; // Filebase默认使用'us-east-1'
@ -36,234 +37,56 @@ async function handles3filebaseRequest(request) {
const timestamp = new Date().toISOString().replace(/[:\-]|\.\d{3}/g, ''); const timestamp = new Date().toISOString().replace(/[:\-]|\.\d{3}/g, '');
const s3FileKey = `${fileName}_${timestamp}`; const s3FileKey = `${fileName}_${timestamp}`;
// 准备请求细节 // 创建 AwsClient 实例
const method = 'PUT'; const aws = new AwsClient({
const canonicalUri = `/${bucketName}/${s3FileKey}`; accessKeyId,
const canonicalQueryString = ''; secretAccessKey,
const payloadHash = await sha256(fileContent); service,
const amzDate = getAmzDate(); region,
const dateStamp = amzDate.substring(0, 8); });
const credentialScope = `${dateStamp}/${region}/${service}/aws4_request`;
// 准备请求头 // 构建上传文件的 URL
let headers = { const url = `https://${host}/${bucketName}/${s3FileKey}`;
Host: host,
'X-Amz-Date': amzDate,
'Content-Type': 'application/octet-stream',
'Content-Length': fileContent.byteLength.toString(),
};
// 创建规范请求 // 发起 PUT 请求上传文件
const signedHeaders = getSignedHeaders(headers); const uploadResponse = await aws.fetch(url, {
const canonicalHeaders = getCanonicalHeaders(headers); method: 'PUT',
const canonicalRequest = [ headers: {
method, 'Content-Type': 'application/octet-stream',
canonicalUri, },
canonicalQueryString,
canonicalHeaders,
signedHeaders,
payloadHash,
].join('\n');
// 创建待签名字符串
const canonicalRequestHash = await sha256(canonicalRequest);
const stringToSign = [
'AWS4-HMAC-SHA256',
amzDate,
credentialScope,
canonicalRequestHash,
].join('\n');
// 计算签名
const signingKey = await getSignatureKey(secretAccessKey, dateStamp, region, service);
const signature = await hmacHex(signingKey, stringToSign);
// 添加授权头
headers['Authorization'] = [
`AWS4-HMAC-SHA256 Credential=${accessKeyId}/${credentialScope}`,
`SignedHeaders=${signedHeaders}`,
`Signature=${signature}`,
].join(', ');
// 发起PUT请求上传文件
const url = `https://${host}${canonicalUri}`;
const uploadResponse = await fetch(url, {
method: method,
headers: headers,
body: fileContent, body: fileContent,
}); });
if (!uploadResponse.ok) { if (!uploadResponse.ok) {
const errorText = await uploadResponse.text(); const errorText = await uploadResponse.text();
console.error(`上传失败: ${uploadResponse.status} - ${errorText}`); return new Response(`Upload failed with status: ${uploadResponse.status}, error: ${errorText}`, { status: uploadResponse.status });
return new Response(`Upload failed with status: ${uploadResponse.status}`, { status: uploadResponse.status });
} }
console.log('文件上传成功!'); // 发起 HEAD 请求获取元数据以获得 CID
const metadataResponse = await aws.fetch(url, {
// 现在获取元数据以获得CID method: 'HEAD',
const metadataResponse = await getObjectMetadata( });
accessKeyId,
secretAccessKey,
bucketName,
s3FileKey,
region,
service,
host
);
if (!metadataResponse.ok) { if (!metadataResponse.ok) {
const errorText = await metadataResponse.text(); const errorText = await metadataResponse.text();
console.error(`获取元数据失败: ${metadataResponse.status} - ${errorText}`); return new Response(`Failed to retrieve metadata with status: ${metadataResponse.status}, error: ${errorText}`, { status: metadataResponse.status });
return new Response(`Failed to retrieve metadata with status: ${metadataResponse.status}`, { status: metadataResponse.status });
} }
// 从元数据中提取CID // 从元数据中提取 CID
const cid = metadataResponse.headers.get('x-amz-meta-cid'); const cid = metadataResponse.headers.get('x-amz-meta-cid');
if (!cid) { if (!cid) {
console.error('在元数据中未找到CID。');
return new Response('CID not found in metadata.', { status: 500 }); return new Response('CID not found in metadata.', { status: 500 });
} }
console.log(`CID: ${cid}`); // 构建访问 URL
// 构建访问URL
const accessUrl = `https://ipfs.filebase.io/ipfs/${cid}`; const accessUrl = `https://ipfs.filebase.io/ipfs/${cid}`;
// 返回访问URL // 返回访问 URL
return new Response(accessUrl, { return new Response(accessUrl, {
status: 200, status: 200,
headers: { 'Content-Type': 'text/plain' }, headers: { 'Content-Type': 'text/plain' },
}); });
} catch (error) { } catch (error) {
console.error('Error:', error); return new Response(`Internal Server Error: ${error.message}`, { status: 500 });
return new Response('Internal Server Error', { status: 500 });
} }
} }
// 辅助函数
function getAmzDate() {
const now = new Date();
return now.toISOString().replace(/[:-]|\.\d{3}/g, '');
}
function getSignedHeaders(headers) {
return Object.keys(headers)
.map(key => key.toLowerCase())
.sort()
.join(';');
}
function getCanonicalHeaders(headers) {
return Object.keys(headers)
.map(key => key.toLowerCase() + ':' + headers[key].trim() + '\n')
.sort()
.join('');
}
async function sha256(message) {
let buffer;
if (message instanceof ArrayBuffer || message instanceof Uint8Array) {
buffer = message;
} else {
const encoder = new TextEncoder();
buffer = encoder.encode(message);
}
const hashBuffer = await crypto.subtle.digest('SHA-256', buffer);
return arrayBufferToHex(hashBuffer);
}
async function hmac(key, message) {
const cryptoKey = await crypto.subtle.importKey(
'raw',
key instanceof Uint8Array ? key : new Uint8Array(key),
{ name: 'HMAC', hash: 'SHA-256' },
false,
['sign']
);
const signature = await crypto.subtle.sign('HMAC', cryptoKey, message);
return signature;
}
async function hmacHex(key, message) {
const encoder = new TextEncoder();
const keyData = key instanceof Uint8Array ? key : encoder.encode(key);
const messageData = message instanceof Uint8Array ? message : encoder.encode(message);
const signature = await hmac(keyData, messageData);
return arrayBufferToHex(signature);
}
function arrayBufferToHex(buffer) {
const byteArray = new Uint8Array(buffer);
return Array.from(byteArray)
.map(byte => byte.toString(16).padStart(2, '0'))
.join('');
}
async function getSignatureKey(key, dateStamp, regionName, serviceName) {
const encoder = new TextEncoder();
const kDate = await hmac(encoder.encode('AWS4' + key), encoder.encode(dateStamp));
const kRegion = await hmac(kDate, encoder.encode(regionName));
const kService = await hmac(kRegion, encoder.encode(serviceName));
const kSigning = await hmac(kService, encoder.encode('aws4_request'));
return kSigning;
}
// 获取对象元数据的函数HEAD请求
async function getObjectMetadata(accessKeyId, secretAccessKey, bucketName, objectKey, region, service, host) {
const method = 'HEAD';
const canonicalUri = `/${bucketName}/${objectKey}`;
const canonicalQueryString = '';
const payloadHash = await sha256('');
const amzDate = getAmzDate();
const dateStamp = amzDate.substring(0, 8);
const credentialScope = `${dateStamp}/${region}/${service}/aws4_request`;
// 准备请求头
let headers = {
Host: host,
'X-Amz-Date': amzDate,
};
// 创建规范请求
const signedHeaders = getSignedHeaders(headers);
const canonicalHeaders = getCanonicalHeaders(headers);
const canonicalRequest = [
method,
canonicalUri,
canonicalQueryString,
canonicalHeaders,
signedHeaders,
payloadHash,
].join('\n');
// 创建待签名字符串
const canonicalRequestHash = await sha256(canonicalRequest);
const stringToSign = [
'AWS4-HMAC-SHA256',
amzDate,
credentialScope,
canonicalRequestHash,
].join('\n');
// 计算签名
const signingKey = await getSignatureKey(secretAccessKey, dateStamp, region, service);
const signature = await hmacHex(signingKey, stringToSign);
// 添加授权头
headers['Authorization'] = [
`AWS4-HMAC-SHA256 Credential=${accessKeyId}/${credentialScope}`,
`SignedHeaders=${signedHeaders}`,
`Signature=${signature}`,
].join(', ');
// 发起HEAD请求
const url = `https://${host}${canonicalUri}`;
const response = await fetch(url, {
method: method,
headers: headers,
});
return response;
}

View File

@ -663,6 +663,8 @@ async function handleRequest(request) {
} }
} }
import { AwsClient } from 'aws4fetch';
async function handles3filebaseRequest(request) { async function handles3filebaseRequest(request) {
if (request.method !== 'POST') { if (request.method !== 'POST') {
return new Response('Method not allowed', { status: 405 }); return new Response('Method not allowed', { status: 405 });
@ -684,8 +686,7 @@ async function handleRequest(request) {
// 检查是否成功解析 // 检查是否成功解析
if (!bucketName || !accessKeyId || !secretAccessKey) { if (!bucketName || !accessKeyId || !secretAccessKey) {
console.error('Failed to parse S3FILEBASE_KEY environment variable.'); return new Response('Server configuration error: Invalid S3FILEBASE_KEY', { status: 500 });
return new Response('Server configuration error.', { status: 500 });
} }
const region = 'us-east-1'; // Filebase默认使用'us-east-1' const region = 'us-east-1'; // Filebase默认使用'us-east-1'
@ -697,235 +698,57 @@ async function handleRequest(request) {
const timestamp = new Date().toISOString().replace(/[:\-]|\.\d{3}/g, ''); const timestamp = new Date().toISOString().replace(/[:\-]|\.\d{3}/g, '');
const s3FileKey = `${fileName}_${timestamp}`; const s3FileKey = `${fileName}_${timestamp}`;
// 准备请求细节 // 创建 AwsClient 实例
const method = 'PUT'; const aws = new AwsClient({
const canonicalUri = `/${bucketName}/${s3FileKey}`; accessKeyId,
const canonicalQueryString = ''; secretAccessKey,
const payloadHash = await sha256(fileContent); service,
const amzDate = getAmzDate(); region,
const dateStamp = amzDate.substring(0, 8); });
const credentialScope = `${dateStamp}/${region}/${service}/aws4_request`;
// 准备请求头 // 构建上传文件的 URL
let headers = { const url = `https://${host}/${bucketName}/${s3FileKey}`;
Host: host,
'X-Amz-Date': amzDate,
'Content-Type': 'application/octet-stream',
'Content-Length': fileContent.byteLength.toString(),
};
// 创建规范请求 // 发起 PUT 请求上传文件
const signedHeaders = getSignedHeaders(headers); const uploadResponse = await aws.fetch(url, {
const canonicalHeaders = getCanonicalHeaders(headers); method: 'PUT',
const canonicalRequest = [ headers: {
method, 'Content-Type': 'application/octet-stream',
canonicalUri, },
canonicalQueryString,
canonicalHeaders,
signedHeaders,
payloadHash,
].join('\n');
// 创建待签名字符串
const canonicalRequestHash = await sha256(canonicalRequest);
const stringToSign = [
'AWS4-HMAC-SHA256',
amzDate,
credentialScope,
canonicalRequestHash,
].join('\n');
// 计算签名
const signingKey = await getSignatureKey(secretAccessKey, dateStamp, region, service);
const signature = await hmacHex(signingKey, stringToSign);
// 添加授权头
headers['Authorization'] = [
`AWS4-HMAC-SHA256 Credential=${accessKeyId}/${credentialScope}`,
`SignedHeaders=${signedHeaders}`,
`Signature=${signature}`,
].join(', ');
// 发起PUT请求上传文件
const url = `https://${host}${canonicalUri}`;
const uploadResponse = await fetch(url, {
method: method,
headers: headers,
body: fileContent, body: fileContent,
}); });
if (!uploadResponse.ok) { if (!uploadResponse.ok) {
const errorText = await uploadResponse.text(); const errorText = await uploadResponse.text();
console.error(`上传失败: ${uploadResponse.status} - ${errorText}`); return new Response(`Upload failed with status: ${uploadResponse.status}, error: ${errorText}`, { status: uploadResponse.status });
return new Response(`Upload failed with status: ${uploadResponse.status}`, { status: uploadResponse.status });
} }
console.log('文件上传成功!'); // 发起 HEAD 请求获取元数据以获得 CID
const metadataResponse = await aws.fetch(url, {
// 现在获取元数据以获得CID method: 'HEAD',
const metadataResponse = await getObjectMetadata( });
accessKeyId,
secretAccessKey,
bucketName,
s3FileKey,
region,
service,
host
);
if (!metadataResponse.ok) { if (!metadataResponse.ok) {
const errorText = await metadataResponse.text(); const errorText = await metadataResponse.text();
console.error(`获取元数据失败: ${metadataResponse.status} - ${errorText}`); return new Response(`Failed to retrieve metadata with status: ${metadataResponse.status}, error: ${errorText}`, { status: metadataResponse.status });
return new Response(`Failed to retrieve metadata with status: ${metadataResponse.status}`, { status: metadataResponse.status });
} }
// 从元数据中提取CID // 从元数据中提取 CID
const cid = metadataResponse.headers.get('x-amz-meta-cid'); const cid = metadataResponse.headers.get('x-amz-meta-cid');
if (!cid) { if (!cid) {
console.error('在元数据中未找到CID。');
return new Response('CID not found in metadata.', { status: 500 }); return new Response('CID not found in metadata.', { status: 500 });
} }
console.log(`CID: ${cid}`); // 构建访问 URL
// 构建访问URL
const accessUrl = `https://ipfs.filebase.io/ipfs/${cid}`; const accessUrl = `https://ipfs.filebase.io/ipfs/${cid}`;
// 返回访问URL // 返回访问 URL
return new Response(accessUrl, { return new Response(accessUrl, {
status: 200, status: 200,
headers: { 'Content-Type': 'text/plain' }, headers: { 'Content-Type': 'text/plain' },
}); });
} catch (error) { } catch (error) {
console.error('Error:', error); return new Response(`Internal Server Error: ${error.message}`, { status: 500 });
return new Response('Internal Server Error', { status: 500 });
} }
} }
// 辅助函数
function getAmzDate() {
const now = new Date();
return now.toISOString().replace(/[:-]|\.\d{3}/g, '');
}
function getSignedHeaders(headers) {
return Object.keys(headers)
.map(key => key.toLowerCase())
.sort()
.join(';');
}
function getCanonicalHeaders(headers) {
return Object.keys(headers)
.map(key => key.toLowerCase() + ':' + headers[key].trim() + '\n')
.sort()
.join('');
}
async function sha256(message) {
let buffer;
if (message instanceof ArrayBuffer || message instanceof Uint8Array) {
buffer = message;
} else {
const encoder = new TextEncoder();
buffer = encoder.encode(message);
}
const hashBuffer = await crypto.subtle.digest('SHA-256', buffer);
return arrayBufferToHex(hashBuffer);
}
async function hmac(key, message) {
const cryptoKey = await crypto.subtle.importKey(
'raw',
key instanceof Uint8Array ? key : new Uint8Array(key),
{ name: 'HMAC', hash: 'SHA-256' },
false,
['sign']
);
const signature = await crypto.subtle.sign('HMAC', cryptoKey, message);
return signature;
}
async function hmacHex(key, message) {
const encoder = new TextEncoder();
const keyData = key instanceof Uint8Array ? key : encoder.encode(key);
const messageData = message instanceof Uint8Array ? message : encoder.encode(message);
const signature = await hmac(keyData, messageData);
return arrayBufferToHex(signature);
}
function arrayBufferToHex(buffer) {
const byteArray = new Uint8Array(buffer);
return Array.from(byteArray)
.map(byte => byte.toString(16).padStart(2, '0'))
.join('');
}
async function getSignatureKey(key, dateStamp, regionName, serviceName) {
const encoder = new TextEncoder();
const kDate = await hmac(encoder.encode('AWS4' + key), encoder.encode(dateStamp));
const kRegion = await hmac(kDate, encoder.encode(regionName));
const kService = await hmac(kRegion, encoder.encode(serviceName));
const kSigning = await hmac(kService, encoder.encode('aws4_request'));
return kSigning;
}
// 获取对象元数据的函数HEAD请求
async function getObjectMetadata(accessKeyId, secretAccessKey, bucketName, objectKey, region, service, host) {
const method = 'HEAD';
const canonicalUri = `/${bucketName}/${objectKey}`;
const canonicalQueryString = '';
const payloadHash = await sha256('');
const amzDate = getAmzDate();
const dateStamp = amzDate.substring(0, 8);
const credentialScope = `${dateStamp}/${region}/${service}/aws4_request`;
// 准备请求头
let headers = {
Host: host,
'X-Amz-Date': amzDate,
};
// 创建规范请求
const signedHeaders = getSignedHeaders(headers);
const canonicalHeaders = getCanonicalHeaders(headers);
const canonicalRequest = [
method,
canonicalUri,
canonicalQueryString,
canonicalHeaders,
signedHeaders,
payloadHash,
].join('\n');
// 创建待签名字符串
const canonicalRequestHash = await sha256(canonicalRequest);
const stringToSign = [
'AWS4-HMAC-SHA256',
amzDate,
credentialScope,
canonicalRequestHash,
].join('\n');
// 计算签名
const signingKey = await getSignatureKey(secretAccessKey, dateStamp, region, service);
const signature = await hmacHex(signingKey, stringToSign);
// 添加授权头
headers['Authorization'] = [
`AWS4-HMAC-SHA256 Credential=${accessKeyId}/${credentialScope}`,
`SignedHeaders=${signedHeaders}`,
`Signature=${signature}`,
].join(', ');
// 发起HEAD请求
const url = `https://${host}${canonicalUri}`;
const response = await fetch(url, {
method: method,
headers: headers,
});
return response;
}