Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
andrewrk committed Nov 24, 2012
0 parents commit 04fc22a
Show file tree
Hide file tree
Showing 5 changed files with 239 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/node_modules
70 changes: 70 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
var knox = require('knox')
, EventEmitter = require('events').EventEmitter
, fs = require('fs');

exports.createClient = function(options) {
return new Client(options);
};

function Client(options) {
this.knox = knox.createClient(options);
}

Client.prototype.upload = function(localFile, remoteFile) {
var uploader = new EventEmitter();
var knoxUpload = this.knox.putFile(localFile, remoteFile, function (err, resp) {
if (err) {
uploader.emit('error', err);
} else if (resp.statusCode === 200) {
uploader.emit('end');
} else {
uploader.emit('error', new Error("s3 http status code " + resp.statusCode));
}
});
knoxUpload.on('progress', function (progress) {
uploader.emit('progress', progress.written, progress.total);
});
return uploader;
};

Client.prototype.download = function(remoteFile, localFile) {
var downloader = new EventEmitter();
var amountDone = 0;
var amountTotal;
var writeStream;
var knoxDownload = this.knox.getFile(remoteFile, function (err, resp) {
if (err) {
downloader.emit('error', err);
} else if (resp.statusCode === 200) {
amountTotal = parseInt(resp.headers['content-length'], 10);
var writeStream = fs.createWriteStream(localFile);
writeStream.on('error', onError);
resp.on('error', onError);
resp.on('end', onSuccess);
resp.on('data', onData);
resp.pipe(writeStream);
} else {
downloader.emit('error', new Error("s3 http status code" + resp.statusCode));
}
function removeListeners() {
writeStream.removeListener('error', onError);
resp.removeListener('error', onError);
resp.removeListener('end', onSuccess);
}
function onError(err) {
removeListeners();
writeStream.destroy();
downloader.emit('error', err);
}
function onSuccess() {
removeListeners();
writeStream.end();
downloader.emit('end');
}
function onData(data) {
amountDone += data.length;
downloader.emit('progress', amountDone, amountTotal);
}
});
return downloader;
};
29 changes: 29 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"name": "s3",
"version": "0.0.0",
"description": "high level amazon s3 client using knox as a backend",
"main": "index.js",
"scripts": {
"test": "mocha"
},
"repository": {
"type": "git",
"url": "https://github.com/superjoe30/node-s3"
},
"keywords": [
"amazon",
"s3",
"high",
"level",
"api"
],
"author": "Andrew Kelley",
"license": "BSD",
"devDependencies": {
"mocha": "~1.7.1",
"mkdirp": "~0.3.4"
},
"dependencies": {
"knox": "~0.4.1"
}
}
44 changes: 44 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
Usage:
------
```js
// configure
var s3 = require('s3');
var client = s3.createClient({
key: "your s3 key",
secret: "your s3 secret",
bucket: "your s3 bucket"
});
// createClient allows any options that knox does.

// upload a file to s3
var uploader = client.upload("some/local/file", "some/remote/file");
uploader.on('error', function(err) {
console.error("unable to upload:", err.stack);
});
uploader.on('progress', function(amountDone, amountTotal) {
console.log("progress", amountDone, amountTotal);
});
uploader.on('end', function() {
console.log("done");
});

// download a file from s3
var downloader = client.download("some/remote/file", "some/local/file");
downloader.on('error', function(err) {
console.error("unable to upload:", err.stack);
});
downloader.on('progress', function(amountDone, amountTotal) {
console.log("progress", amountDone, amountTotal);
});
downloader.on('end', function() {
console.log("done");
});
```

This module uses [knox](https://github.com/LearnBoost/knox) as a backend. If
you want to do more low-level things, use knox for those things. It's ok to use
both.

Testing:
--------
S3_KEY=<valid_s3_key> S3_SECRET=<valid_s3_secret> S3_BUCKET=<valid_s3_bucket> npm test
95 changes: 95 additions & 0 deletions test/test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
var s3 = require('../')
, path = require('path')
, assert = require('assert')
, fs = require('fs')
, mkdirp = require('mkdirp')
, crypto = require('crypto')
, tempDir = path.join(__dirname, 'tmp')
, localFile = path.join(tempDir, 'random')
, remoteFile = "/node-s3-test/file.png"

function createClient() {
return s3.createClient({
key: process.env.S3_KEY,
secret: process.env.S3_SECRET,
bucket: process.env.S3_BUCKET
});
}

function createBigFile(cb) {
mkdirp(tempDir, function(err) {
if (err) return cb(err);
var md5sum = crypto.createHash('md5');
var out = fs.createWriteStream(localFile);
out.on('error', function(err) {
cb(err);
});
out.on('close', function() {
cb(null, md5sum.digest('hex'));
});
var str = "abcdefghijklmnopqrstuvwxyz";
for (var i = 0; i < 4000; ++i) {
out.write(str);
md5sum.update(str);
}
out.end();
});
}

describe("s3", function () {
var hexdigest;
it("uploads", function(done) {
createBigFile(function (err, _hexdigest) {
if (err) return done(err);
hexdigest = _hexdigest;
var client = createClient();
var uploader = client.upload(localFile, remoteFile);
uploader.on('error', done);
var progress = 0;
var progressEventCount = 0;
uploader.on('progress', function(amountDone, amountTotal) {
var newProgress = amountDone / amountTotal;
progressEventCount += 1;
assert(newProgress >= progress, "old progress: " + progress + ", new progress: " + newProgress);
progress = newProgress;
});
uploader.on('end', function() {
assert.strictEqual(progress, 1);
assert(progressEventCount >= 3, "expected at least 3 progress events. got " + progressEventCount);
done();
});
});
});
it("dowloads", function(done) {
fs.unlink(localFile, function(err) {
if (err) return done(err);
var client = createClient();
var downloader = client.download(remoteFile, localFile);
downloader.on('error', done);
var progress = 0;
var progressEventCount = 0;
downloader.on('progress', function(amountDone, amountTotal) {
var newProgress = amountDone / amountTotal;
progressEventCount += 1;
assert(newProgress >= progress, "old progress: " + progress + ", new progress: " + newProgress);
progress = newProgress;
});
downloader.on('end', function() {
assert.strictEqual(progress, 1);
assert(progressEventCount >= 3, "expected at least 3 progress events. got " + progressEventCount);
var reader = fs.createReadStream(localFile);
var md5sum = crypto.createHash('md5');
reader.on('data', function(data) {
md5sum.update(data);
});
reader.on('error', function (err) {
done(err);
});
reader.on('end', function() {
assert.strictEqual(md5sum.digest('hex'), hexdigest);
fs.unlink(localFile, done);
});
});
});
});
});

0 comments on commit 04fc22a

Please sign in to comment.