Skip to content

Instantly share code, notes, and snippets.

@cmawhorter
Created October 6, 2015 18:03
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cmawhorter/b706e45ba88e43bc379c to your computer and use it in GitHub Desktop.
Save cmawhorter/b706e45ba88e43bc379c to your computer and use it in GitHub Desktop.
Help workaround discrepancy between OS directory names and S3 key name collisions
'use strict';
// Script to workaround discrepancies in s3 <=> os file and directory names.
// It allows you to host your static website on s3 without trailing slashes.
// e.g. example.com/products and example.com/products/mugs
if (!process.argv[2]) throw new Error('Must pass bucket as argument');
var options = {
target: '.',
bucket: process.argv[2],
region: process.env.AWS_DEFAULT_REGION,
key: process.env.AWS_ACCESS_KEY_ID,
secret: process.env.AWS_SECRET_ACCESS_KEY,
bucketPrefix: '',
remove: true,
concurrency: 12,
};
console.log('Starting with key %s...', options.key);
var http = require('http')
, https = require('https');
var AWS = require('aws-sdk')
, async = require('async');
http.globalAgent.maxSockets = https.globalAgent.maxSockets = 72;
AWS.config.update({ accessKeyId: options.key, secretAccessKey: options.secret, region: options.region });
AWS.events.on('httpError', function() {
if (this.response.error && this.response.error.code === 'UnknownEndpoint') {
this.response.error.retryable = true;
}
});
var s3 = new AWS.S3();
function getMimeType(prefix) {
var ext = prefix.split('.').pop();
switch (ext.toLowerCase()) {
default:
case 'html':
case 'aspx':
return 'text/html';
}
return;
}
function renameObject(prefix, callback) {
var renamedPrefix = prefix.replace('.collision.', '');
var mimeType = getMimeType(prefix);
console.log('renameObject %s -> %s; mime = %s', prefix, renamedPrefix, mimeType);
var params = {
Bucket: options.bucket,
CopySource: options.bucket + '/' + prefix,
Key: renamedPrefix,
MetadataDirective: 'REPLACE',
};
if (mimeType) {
params.ContentType = mimeType;
}
s3.copyObject(params, function(err, data) {
if (err) return callback(err);
console.log('\t-> copied (%s)', prefix);
s3.deleteObject({
Bucket: options.bucket,
Key: prefix,
}, function(err, data) {
if (err) return callback(err);
console.log('\t-> removed (%s)', prefix);
callback();
});
});
}
function collisionsOnly(el) {
return el.split('/').pop().indexOf('.collision.') === 0;
}
function renameAll(prefix) {
return async.apply(renameObject, prefix);
}
var allKeys = [];
// I copied this function from some SO question and tweaked it
var listAllKeys = function listAllKeys(marker, cb) {
s3.listObjects({ Bucket: options.bucket, Marker: marker }, function(err, data) {
if (err) {
return cb(err);
}
Array.prototype.push.apply(allKeys, data.Contents.map(function(el) {
return el.Key;
}));
if(data.IsTruncated) {
listAllKeys(data.Contents.slice(-1)[0].Key, cb);
}
else {
cb();
}
});
};
listAllKeys(options.bucketPrefix, function(err) {
if (err) {
return callback(err);
}
async.parallelLimit(allKeys.filter(collisionsOnly).map(renameAll), 12, function(err) {
if (err) throw err;
console.log('done!');
process.exit();
});
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment