1

我正在学习 cloudflare 工作者,我需要通过数字海洋将对象存储在空间中。

如果您不知道,cloudflare worker 不能使用常规的aws-sdkjs 库,因为它使用XMLHttpRequest了 worker 的隔离区不可用的库。

我正在使用空间 API 进行手动 PUT,并且可以毫无问题地将纯文本对象上传到存储桶。

当我尝试使用构造函数放置图像时,问题就开始了new Buffer.from(...some image content as base64...,'base64'),我从空间 API 得到的错误是XAmzContentSHA256Mismatch,我认为问题是当我将正文散列到sha256使用crypto-js库时,但我不知道为什么。

import sha256 from 'crypto-js/sha256'
import hmacSha256 from 'crypto-js/hmac-sha256'

export default class SpacesClient {
    constructor( config ){
        this.accessKey = config.accessKey;
        this.secretKey = config.secretKey;
        this.region = config.region;
        this.domain = config.domain || 'digitaloceanspaces.com';
    }

    getSignatureKey(key, dateStamp, regionName, serviceName) {
        let keyDate = hmacSha256(dateStamp, "AWS4" + key);
        let keyRegion = hmacSha256(regionName, keyDate);
        let keyService = hmacSha256(serviceName, keyRegion);
        return hmacSha256("aws4_request", keyService);
    }


    //TODO: everything works great if body is text, the next step is to check why it doesn't work when it is Buffer
    signAndSendRequest( { method , bucket , path , body , acl , contentType } ){
        const amzdate = new Date().toISOString().replace(/[:-]|\.\d{3}/g, '');
        const datestamp = amzdate.slice(0, 8)

        const service = 's3';
        const host = (this.domain !== 'digitaloceanspaces.com')
            ? `${bucket}.${service}.${this.region}.${this.domain}`
            : `${bucket}.${this.region}.${this.domain}`

        const endpoint = `https://${host}${path}`;
        acl = acl === 'public-read' ? 'public-read' : 'private';

        const canonicalUri = path;
        const canonicalQuerystring = '';
        const payloadHash = sha256(body).toString();
        const canonicalHeaders = `content-type:${contentType}\nhost:${host}\nx-amz-acl:${acl}\nx-amz-content-sha256:${payloadHash}\nx-amz-date:${amzdate}\n`;
        const signedHeaders = 'content-type;host;x-amz-acl;x-amz-content-sha256;x-amz-date';
        const algorithm = 'AWS4-HMAC-SHA256';

        const canonicalRequest = `${method}\n${canonicalUri}\n${canonicalQuerystring}\n${canonicalHeaders}\n${signedHeaders}\n${payloadHash}`;

        const credentialScope = `${datestamp}/${this.region}/${service}/aws4_request`;
        const stringToSign = `${algorithm}\n${amzdate}\n${credentialScope}\n${sha256(canonicalRequest)}`;

        const signingKey = this.getSignatureKey(this.secretKey, datestamp, this.region, service);
        const signature = hmacSha256(stringToSign, signingKey);

        const authorizationHeader = `${algorithm} Credential=${this.accessKey}/${credentialScope},SignedHeaders=${signedHeaders},Signature=${signature}`;

        const params = {
            method: method,
            headers: {
                'Authorization': authorizationHeader,
                'content-type': contentType,
                'x-amz-acl': acl,
                'x-amz-content-sha256': payloadHash,
                'x-amz-date': amzdate,
            },
        };

        if ( body ) params.body = body;

        return fetch( endpoint, params );
    }

    getObject( params ) {
        return this.signAndSendRequest( { method: 'GET' , ...params } );
    }

    putObject(params) {
        return this.signAndSendRequest( { method: 'PUT' , ...params } );
    }

    deleteObject(params) {
        return this.signAndSendRequest( { method: 'DELETE' , ...params } );
    }
}

当 body 变量是字符串并且 contentType 是时,它的效果很好text/plain

任何帮助,将不胜感激。

4

0 回答 0