2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

GASでURLから画像をS3にをアップロードする

Last updated at Posted at 2022-02-24

経緯

画像URLからGASで画像をS3にをアップロードしたら画像ファイルが壊れていて開けなかったので、
その時の対応をまとめました。

S3ライブラリ

下記のライブラリIDを使用
1Qx-smYQLJ2B6ae7Pncbf_8QdFaNm0f-br4pbDg0DXsJ9mZJPdFcIEkw_

実装

const s3 = S3.getInstance("your_accessKeyId", "your_secretAccessKey");

var blob = UrlFetchApp.fetch("https://xxxxx.com/img/2001/2001.jpg").getBlob();

s3.putObject("bucket_name", "/img/2001/2001.jpg", blob, {logRequests:true});

この実装だとS3にアップロードされたファイルをダウンロードして開こうとするとエラーになる

原因

s3.putObjectが画像に対応していなく文字列になってしまうため

解決

(転写)

  • S3.gs line 142: change request.setContent(object.getDataAsString()); to request.setContent(object);
  • S3Request.gs line 60: remove the line if (typeof content != 'string') throw 'content must be passed as a string'
  • S3Request.gs line 393: replace the function with the following:
S3Request.prototype.hexEncodedHash = function(string) {
  if(typeof string === "string")
    return this.hex(Utilities.computeDigest(Utilities.DigestAlgorithm.SHA_256, string, Utilities.Charset.UTF_8));
  else
    return this.hex(Utilities.computeDigest(Utilities.DigestAlgorithm.SHA_256, string.getBytes()));
}

S3ライブラリの修正方法

下記のファイルの内容をコピーしてGASに貼り付けて上記の解決方法を適用する
https://github.com/eschultink/S3-for-Google-Apps-Script/blob/master/S3.gs
https://github.com/eschultink/S3-for-Google-Apps-Script/blob/master/S3Request.gs

実装(修正後)

  • S3ライブラリを同一ファイルにコピーし上記解決方法を適用
  • S3.getInstance -> getInstance に修正
function myFunction() {
  
    // const s3 = S3.getInstance("your_accessKeyId", "your_secretAccessKey");
    const s3 = getInstance("your_accessKeyId", "your_secretAccessKey");

    var blob = UrlFetchApp.fetch("https://xxxxx.com/img/2001/2001.jpg").getBlob();

    s3.putObject("bucket_name", "/img/2001/2001.jpg", blob, {logRequests:true});

}

/* 
 * very basic AWS S3 Client library for Google Apps Script
 * @author Erik Schultink <erik@engetc.com>
 * includes create/delete buckets, create/read/delete objects. very limited support for any optional params.
 * 
 * @see http://engetc.com/projects/amazon-s3-api-binding-for-google-apps-script/
 */

/**
 * @license Copyright 2014-15 Eng Etc LLC - All Rights Reserved
 *
 * LICENSE (Modified BSD) - Redistribution and use in source and binary forms, with or without modification, 
 * are permitted provided that the following conditions are met:
 *   1) Redistributions of source code must retain the above copyright notice, this list of conditions and 
 *      the following disclaimer.
 *   2) Redistributions in binary form must reproduce the above copyright notice, this list of conditions 
 *      and the following disclaimer in the documentation and/or other materials provided with the 
 *      distribution.
 *   3) Neither the name of the Eng Etc LLC, S3-for-Google-Apps-Script, nor the names of its contributors may be used to endorse or 
 *      promote products derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED 
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 
 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ENG ETC LLC BE LIABLE FOR ANY DIRECT, INDIRECT, 
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 
 * SUCH DAMAGE.
 */

//Body of the library


/* constructs an S3 service
 *
 * @constructor
 * @param {string} accessKeyId your AWS AccessKeyId
 * @param {string} secretAccessKey your AWS SecretAccessKey
 * @param {Object} options key-value object of options, unused
 *
 * @return {S3}
 */
function getInstance(accessKeyId, secretAccessKey, options) {
  return new S3(accessKeyId, secretAccessKey, options);
}

/* constructs an S3 service
 *
 * @constructor
 * @param {string} accessKeyId your AWS AccessKeyId
 * @param {string} secretAccessKey your AWS SecretAccessKey
 * @param {Object} options key-value object of options, unused
 */
function S3(accessKeyId, secretAccessKey, options) {
  if (typeof accessKeyId !== 'string') throw "Must pass accessKeyId to S3 constructor";
  if (typeof secretAccessKey !== 'string') throw "Must pass secretAcessKey to S3 constructor";
  
  this.accessKeyId = accessKeyId;
  this.secretAccessKey = secretAccessKey;
  this.options = options | {};
}



/* creates bucket in S3
 *
 * @param {string} bucket name of bucket
 * @param {Object} options optional parameters to create request; supports x-amz-acl
 * @throws {Object} AwsError on failure
 * @return void
 */
S3.prototype.createBucket = function (bucket, options) {
  options = options || {}; 
  
  
  var request = new S3Request(this);
  request.setHttpMethod('PUT');
  
  //this is dumb and is optional from AWS perspective
  //but UrlFetchApp will default a Content-Type header to application/xml-www-form-url-encoded or whatever, which 
  //screws up the signature of the request
  request.setContentType('text/plain');
  
  //support setting of ACL
  if (typeof options["x-amz-acl"] == 'undefined') {
    options["x-amz-acl"] = "private";
  }
  request.addHeader("x-amz-acl", options["x-amz-acl"]);
  
  request.setBucket(bucket);
  
  request.execute(options);
  
};

/* deletes bucket from S3 
 *
 * @param {string} bucket name of bucket
 * @param {Object} options optional parameters to delete request
 * @throws {Object} AwsError on failure
 * @return void
 */
S3.prototype.deleteBucket = function (bucket, options) {
  options = options || {};

  var request = new S3Request(this);
  request.setHttpMethod('DELETE');
  
  request.setBucket(bucket);
  request.execute(options);
};

/* puts an object into S3 bucket
 * 
 * @param {string} bucket 
 * @param {string} objectName name to uniquely identify object within bucket
 * @param {string} object byte sequence that is object's content
 * @param {Object} options optional parameters
 * @throws {Object} AwsError on failure
 * @return void
 */
S3.prototype.putObject = function (bucket, objectName, object, options) {
  options = options || {};

  var request = new S3Request(this);
  request.setHttpMethod('PUT');
  request.setBucket(bucket);
  request.setObjectName(objectName);
  
  var failedBlobDuckTest = !(typeof object.copyBlob == 'function' &&
                      typeof object.getDataAsString == 'function' &&
                      typeof object.getContentType == 'function'
                      );
  
  //wrap object in a Blob if it doesn't appear to be one
  if (failedBlobDuckTest) {
    object = Utilities.newBlob(JSON.stringify(object), "application/json");
    object.setName(objectName);
  }
  
  // request.setContent(object.getDataAsString());
  request.setContent(object);
  request.setContentType(object.getContentType());
  
  request.execute(options);  
};

/* gets object from S3 bucket
 *
 * @param {string} bucket name of bucket
 * @param {string} objectName name that uniquely identifies object within bucket
 * @param {Object} options optional parameters for get request (unused)
 * @throws {Object} AwsError on failure
 * @return {Blob|Object} data value, converted from JSON or as a Blob if it was something else; null if it doesn't exist
 */
S3.prototype.getObject = function (bucket, objectName, options) {
  options = options || {};
  
  var request = new S3Request(this);
  request.setHttpMethod('GET');
  
  request.setBucket(bucket);
  request.setObjectName(objectName);
  try {
    var responseBlob = request.execute(options).getBlob();
  } catch (e) {
    if (e.name == "AwsError" && e.code == 'NoSuchKey') {
      return null;
    } else {
      //some other type of error, rethrow
      throw e; 
    }
  }
  
  //not sure this is better to put here, rather than in S3Request class
  if (responseBlob.getContentType() == "application/json") {
     return JSON.parse(responseBlob.getDataAsString());
  }
  return responseBlob;
};

/* deletes object from S3 bucket
 *
 * @param {string} bucket bucket name
 * @param {string} objectName name that uniquely identifies object within bucket
 * @param {Object} options optional parameters to delete request, unused
 * @throws {Object} AwsError on failure
 * @return void
 */
S3.prototype.deleteObject = function (bucket, objectName, options) {
  options = options || {};  
  
  var request = new S3Request(this);
  request.setHttpMethod('DELETE');
  
  request.setBucket(bucket);
  request.setObjectName(objectName);
  
  request.execute(options);  
};


//for debugging
S3.prototype.getLastExchangeLog = function() {
  return this.lastExchangeLog; 
}

/*
 * helper to format log entry about HTTP request/response
 * 
 * @param {Object} request object, from UrlFetchApp.getRequest()
 * @param {goog.HTTPResponse} response object, from UrlFetchApp
 */
S3.prototype.logExchange_ = function(request, response) {
  var logContent = "";
  logContent += "\n-- REQUEST --\n";
  for (i in request) {
    if (typeof request[i] == 'string' && request[i].length > 1000) {
      //truncate to avoid making log unreadable
      request[i] = request[i].slice(0, 1000) + " ... [TRUNCATED]"; 
    }
    logContent += Utilities.formatString("\t%s: %s\n", i, request[i]);
  }
    
  logContent += "-- RESPONSE --\n";
  logContent += "HTTP Status Code: " + response.getResponseCode() + "\n";
  logContent += "Headers:\n";
  
  var headers = response.getHeaders();
  for (i in headers) {
    logContent += Utilities.formatString("\t%s: %s\n", i, headers[i]);
  }
  logContent += "Body:\n" + response.getContentText();
  this.lastExchangeLog = logContent;
}

/*
 * Most code of AWS Signature Version 4 is ported from the aws-sdk-js
 * https://github.com/aws/aws-sdk-js/blob/7cc9ae5b0d7b2935fa69dee945d5f3e6e638c660/lib/signers/v4.js
 * 
 */

/* constructs an S3Request to an S3 service
 *
 * @constructor
 * @param {S3} service S3 service to which this request will be sent
 */
function S3Request(service) {
  this.service = service;

  this.httpMethod = "GET";
  this.contentType = "";
  this.content = ""; //content of the HTTP request
  this.bucket = ""; //gets turned into host (bucketName.s3.amazonaws.com)
  this.objectName = "";
  this.headers = {};

  this.date = new Date();
  this.serviceName = 's3';
  // this.region = 'us-east-1';
  this.region = 'ap-northeast-1';
  this.expiresHeader = 'presigned-expires';
  this.extQueryString = '';
}

/* sets contenetType of the request
 * @param {string} contentType mime-type, based on RFC, indicated how content is encoded
 * @throws {string} message if invalid input
 * @return {S3Request} this request, for chaining
 */
S3Request.prototype.setContentType = function (contentType) {
  if (typeof contentType != 'string') throw 'contentType must be passed as a string';
  this.contentType = contentType;
  return this;
};

S3Request.prototype.getContentType = function () {
  if (this.contentType) {
    return this.contentType;
  } else {
    //if no contentType has been explicitly set, default based on HTTP methods
    if (this.httpMethod == "PUT" || this.httpMethod == "POST") {
      //UrlFetchApp defaults to this for these HTTP methods
      return "application/x-www-form-urlencoded";
    }
  }
  return "";
}


/* sets content of request
 * @param {string} content request content encoded as a string
 * @throws {string} message if invalid input
 * @return {S3Request} this request, for chaining
 */
S3Request.prototype.setContent = function(content) {
  // if (typeof content != 'string') throw 'content must be passed as a string'
  this.content = content;
  return this;
};

/* sets Http method for request
 * @param {string} method http method for request
 * @throws {string} message if invalid input
 * @return {S3Request} this request, for chaining
 */
S3Request.prototype.setHttpMethod = function(method) {
  if (typeof method != 'string') throw "http method must be string";
  this.httpMethod = method;
  return this;
};

/* sets bucket name for the request
 * @param {string} bucket name of bucket on which request operates
 * @throws {string} message if invalid input
 * @return {S3Request} this request, for chaining
 */
S3Request.prototype.setBucket = function(bucket) {
  if (typeof bucket != 'string') throw "bucket name must be string";
  this.bucket = bucket;
  return this;
};
/* sets objectName (key) for request
 * @param {string} objectName name that uniquely identifies object within bucket
 * @throws {string} message if invalid input
 * @return {S3Request} this request, for chaining
 */
S3Request.prototype.setObjectName = function(objectName) {
  if (typeof objectName != 'string') throw "objectName must be string";
  this.objectName = objectName;
  return this;
};


/* adds HTTP header to S3 request (see AWS S3 REST api documentation for possible values)
 *
 * @param {string} name Header name
 * @param {string} value Header value
 * @throws {string} message if invalid input
 * @return {S3Request} this object, for chaining
 */
S3Request.prototype.addHeader = function(name, value) {
  if (typeof name != 'string') throw "header name must be string";
  if (typeof value != 'string') throw "header value must be string";
  this.headers[name] = encodeURIComponent(value);
  return this;
};

S3Request.prototype._getUrl = function() {
  return "https://" + this.bucket.toLowerCase() + ".s3." + this.region + ".amazonaws.com/" + this.objectName;
};
/* gets Url for S3 request
 * @return {string} url to which request will be sent
 */
S3Request.prototype.getUrl = function() {
  return this._getUrl() + this.extQueryString;
};
/* executes the S3 request and returns HttpResponse
 *
 * Supported options:
 *   logRequests - log requests (and responses) will be logged to Apps Script's Logger. default false.
 *   echoRequestToUrl - also send the request to this URL (useful for debugging Apps Script weirdness)
 *
 * @param {Object} options object with properties corresponding to option values; see documentation
 * @throws {Object} AwsError on failure
 * @returns {goog.UrlFetchApp.HttpResponse}
 */
S3Request.prototype.execute = function(options) {
  options = options || {};
  for (var key in options) {
    var lowerKey = key.toLowerCase()
    if (lowerKey.indexOf('x-amz-') === 0) {
      this.addHeader(key, options[key])
    }
  }

  delete this.headers['Authorization'];
  delete this.headers['Date'];
  delete this.headers['X-Amz-Date'];
  this.headers['X-Amz-Content-Sha256'] = this.hexEncodedBodyHash();
  this.headers['Host'] = this._getUrl().replace(/https?:\/\/(.+amazonaws\.com).*/, '$1');

  var credentials = {
    accessKeyId: this.service.accessKeyId,
    secretAccessKey: this.service.secretAccessKey,
    sessionToken: options.sessionToken
  }

  this.addAuthorization(credentials, this.date)
  // To avoid conflict with UrlFetchApp#fetch. UrlFetchApp#fetch adds a Host header.
  delete this.headers['Host']

  var params = {
    method: this.httpMethod,
    payload: this.content,
    headers: this.headers,
    muteHttpExceptions: true //get error content in the response
  }

  //only add a ContentType header if non-empty (although should be OK either way)
  if (this.getContentType()) {
    params.contentType = this.getContentType();
  }

  var response = UrlFetchApp.fetch(this.getUrl(), params);

  //debugging stuff
  var request = UrlFetchApp.getRequest(this.getUrl(), params);

  //Log request and response
  this.lastExchangeLog = this.service.logExchange_(request, response);
  if (options.logRequests) {
    Logger.log(this.service.getLastExchangeLog());
  }

  //used in case you want to peak at the actual raw HTTP request coming out of Google's UrlFetchApp infrastructure
  if (options.echoRequestToUrl) {
    UrlFetchApp.fetch(options.echoRequestToUrl, params);
  }

  //check for error codes (AWS uses variants of 200s for flavors of success)
  if (response.getResponseCode() > 299) {
    //convert XML error response from AWS into JS object, and give it a name
    var error = {};
    error.name = "AwsError";
    try {
      var errorXmlElements = XmlService.parse(response.getContentText()).getRootElement().getChildren();
      for (i in errorXmlElements) {
        var name = errorXmlElements[i].getName();
        name = name.charAt(0).toLowerCase() + name.slice(1);
        error[name] = errorXmlElements[i].getText();
      }
      error.toString = function() { return "AWS Error - "+this.code+": "+this.message; };
      error.httpRequestLog = this.service.getLastExchangeLog();
    } catch (e) {
      //error parsing XML error response from AWS (will obscure actual error)
      error.message = "AWS returned HTTP code " + response.getResponseCode() + ", but error content could not be parsed."
      error.toString = function () { return this.message; };
      error.httpRequestLog = this.service.getLastExchangeLog();
    }
    throw error;
  }

  return response;
};

S3Request.prototype.addAuthorization = function(credentials, date) {
  var datetime = date.toISOString().replace(/[:\-]|\.\d{3}/g, '');
  if (this.isPresigned()) {
    this.updateForPresigned(credentials, datetime);
  } else {
    this.addHeaders(credentials, datetime);
  }
  this.headers['Authorization'] = this.authorization(credentials, datetime)
}

S3Request.prototype.addHeaders = function (credentials, datetime) {
  this.headers['X-Amz-Date'] = datetime;
  if (credentials.sessionToken) {
    this.headers['x-amz-security-token'] = credentials.sessionToken;
  }
}

S3Request.prototype.updateForPresigned = function(credentials, datetime) {
  var credString = this.credentialString(datetime);
  var qs = {
    'X-Amz-Date': datetime,
    'X-Amz-Algorithm': 'AWS4-HMAC-SHA256',
    'X-Amz-Credential': credentials.accessKeyId + '/' + credString,
    'X-Amz-Expires': this.headers[this.expiresHeader],
    'X-Amz-SignedHeaders': this.signedHeaders()
  };

  if (credentials.sessionToken) {
    qs['X-Amz-Security-Token'] = credentials.sessionToken;
  }

  if (this.headers['Content-Type']) {
    qs['Content-Type'] = this.headers['Content-Type'];
  }
  if (this.headers['Content-MD5']) {
    qs['Content-MD5'] = this.headers['Content-MD5'];
  }
  if (this.headers['Cache-Control']) {
    qs['Cache-Control'] = this.headers['Cache-Control'];
  }

  for (var key in this.headers) {
    if (key === this.expiresHeader) continue;
    if (this.isSignableHeader(key)) {
      var lowerKey = key.toLowerCase();
      // Metadata should be normalized
      if (lowerKey.indexOf('x-amz-meta-') === 0) {
        qs[lowerKey] = this.headers[key];
      } else if (lowerKey.indexOf('x-amz-') === 0) {
        qs[key] = this.headers[key];
      }
    }
  }

  var sep = this._getUrl().indexOf('?') >= 0 ? '&' : '?';
  var queryParamsToString = function(params) {
    var items = [];
    for (var key in params) {
      var value = params[key];
      var ename = encodeURIComponent(key);
      if (Array.isArray(value)) {
        var vals = [];
        for(var i in value) { vals.push(encodeURIComponent(value[i])) }
        items.push(ename + '=' + vals.sort().join('&' + ename + '='))
      } else {
        items.push(ename + '=' + encodeURIComponent(value))
      }
    }
    return items.sort().join('&')
  }
  this.extQueryString += sep + queryParamsToString(qs);
}

S3Request.prototype.authorization = function(credentials, datetime) {
  var parts = [];
  var credString = this.credentialString(datetime);
  parts.push('AWS4-HMAC-SHA256 Credential=' + credentials.accessKeyId + '/' + credString);
  parts.push('SignedHeaders=' + this.signedHeaders());
  parts.push('Signature=' + this.signature(credentials, datetime));
  return parts.join(', ');
}

S3Request.prototype.signature = function(credentials, datetime) {
  var sigingKey = this.getSignatureKey(
    credentials.secretAccessKey,
    datetime.substr(0, 8),
    this.region,
    this.serviceName
  )
  var signature = Utilities.computeHmacSha256Signature(Utilities.newBlob(this.stringToSign(datetime)).getBytes(), sigingKey)
  return this.hex(signature)
}

S3Request.prototype.hex = function(values) {
  return values.reduce(function(str, chr){
    chr = (chr < 0 ? chr + 256 : chr).toString(16);
    return str + (chr.length == 1 ? '0' : '') + chr;
  }, '');
}

S3Request.prototype.getSignatureKey = function(key, dateStamp, regionName, serviceName) {
  var kDate = Utilities.computeHmacSha256Signature(dateStamp, "AWS4" + key);
  var kRegion = Utilities.computeHmacSha256Signature(Utilities.newBlob(regionName).getBytes(), kDate);
  var kService = Utilities.computeHmacSha256Signature(Utilities.newBlob(serviceName).getBytes(), kRegion);
  var kSigning = Utilities.computeHmacSha256Signature(Utilities.newBlob("aws4_request").getBytes(), kService);
  return kSigning;
}

S3Request.prototype.stringToSign = function(datetime) {
  var parts = [];
  parts.push('AWS4-HMAC-SHA256');
  parts.push(datetime);
  parts.push(this.credentialString(datetime));
  parts.push(this.hexEncodedHash(this.canonicalString()));
  return parts.join('\n');
}

S3Request.prototype.canonicalString = function() {
  var parts = [];
  var [base, search] = this.getUrl().split("?", 2)
  parts.push(this.httpMethod);
  parts.push(this.canonicalUri(base));
  parts.push(this.canonicalQueryString(search));
  parts.push(this.canonicalHeaders() + '\n');
  parts.push(this.signedHeaders());
  parts.push(this.hexEncodedBodyHash());
  return parts.join('\n');
}

S3Request.prototype.canonicalUri = function(uri) {
  var m = uri.match(/https?:\/\/(.+)\.s3.*\.amazonaws\.com\/(.+)$/);
  var object = m ? m[2] : ""
  return "/" + encodeURIComponent(object).replace(/%2F/ig, '/')
}

S3Request.prototype.canonicalQueryString = function(values) {
  if (!values) return ""
  var parts = [];
  var items = values.split("&");
  for (var i in items) {
    var [key, value] = items[i].split("=")
    parts.push(encodeURIComponent(key.toLowerCase()) + "=" + encodeURIComponent(value))
  }
  return parts.sort().join("&")
}

S3Request.prototype.canonicalHeaders = function() {
  var parts = [];
  for (var item in this.headers) {
    var key = item.toLowerCase();
    if (this.isSignableHeader(key)) {
      var header = key + ":" + this.canonicalHeaderValues(this.headers[item].toString())
      parts.push(header)
    }
  }
  return parts.sort().join("\n")
}

S3Request.prototype.canonicalHeaderValues = function(values) {
  return values.replace(/\s+/g, " ").trim();
}

S3Request.prototype.signedHeaders = function() {
  var keys = [];
  for (var key in this.headers) {
    key = key.toLowerCase();
    if (this.isSignableHeader(key)) {
      keys.push(key);
    }
  }
  return keys.sort().join(';');
}

S3Request.prototype.credentialString = function(datetime) {
  return [
    datetime.substr(0, 8),
    this.region,
    this.serviceName,
    'aws4_request'
  ].join('/');
}

// S3Request.prototype.hexEncodedHash = function(string) {
//   return this.hex(Utilities.computeDigest(Utilities.DigestAlgorithm.SHA_256, string, Utilities.Charset.UTF_8));
// }
S3Request.prototype.hexEncodedHash = function(string) {
  if(typeof string === "string")
    return this.hex(Utilities.computeDigest(Utilities.DigestAlgorithm.SHA_256, string, Utilities.Charset.UTF_8));
  else
    return this.hex(Utilities.computeDigest(Utilities.DigestAlgorithm.SHA_256, string.getBytes()));
}

S3Request.prototype.hexEncodedBodyHash = function() {
  if (this.isPresigned() && !this.content.length) {
    return 'UNSIGNED-PAYLOAD'
  } else if (this.headers['X-Amz-Content-Sha256']) {
    return this.headers['X-Amz-Content-Sha256']
  } else {
    return this.hexEncodedHash(this.content || '')
  }
}

S3Request.prototype.isSignableHeader = function(key) {
  var lowerKey = key.toLowerCase()
  if (lowerKey.indexOf('x-amz-') === 0) return true;
  var unsignableHeaders = [
    'authorization',
    'content-type',
    'content-length',
    'user-agent',
    this.expiresHeader,
    'expect',
    'x-amzn-trace-id'
  ];
  return unsignableHeaders.indexOf(lowerKey) < 0
}

S3Request.prototype.isPresigned = function() {
  return this.headers[this.expiresHeader] ? true : false;
}

参考にしたリンク

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?