node.js Amazon s3 how to check file exists

Problem

I'm trying to figure out how to do the equivalent of fs.exists() on S3.

I succeed in getting the contents of the bucket, but I cannot figure out how to ask S3 if a specific file exists in the bucket (I don't want to download the entire list of files and check client-side, because there are too many files).

For reference: AWS2JS documentation on S3.GET

var s3 = require('aws2js').load('s3', appConfig.awsAccessKeyId, appConfig.awsSecretAccessKey);  
s3.setBucket(appConfig.awsBucketName);

            var folder = encodeURI('reports');
            var url = '?prefix=' + folder;

            s3.get(url,{Contents: null, Key: 'reports/emot_cat_r1180341142.png'},'xml', function (error, data) {
                console.log("Error",error);
                console.log("Data",data);
                true.should.be.true;
                done();
            });

The output

{ Name: 'umusergen', Prefix: 'reports', Marker: {}, MaxKeys: '1000', IsTruncated: 'false', Contents: [ { Key: 'reports/', LastModified: '2013-06-16T17:44:25.000Z', ETag: '"d41d8cd98f00b204e9800998ecf8427e"', Size: '0', Owner: [Object], StorageClass: 'STANDARD' }, { Key: 'reports/emot_cat_r1180341142.png', LastModified: '2013-06-16T23:18:59.000Z', ETag: '"0b04aa9146d36a447019b1aa94be7eb3"', Size: '26374', Owner: [Object], StorageClass: 'STANDARD' }, { Key: 'reports/polarity_r1180341142.png', LastModified: '2013-06-16T23:19:00.000Z', ETag: '"22fd521e0a5e858040082d0d47078532"', Size: '23091', Owner: [Object], StorageClass: 'STANDARD' }, { Key: 'reports/wordcloud_r1180341142.png', LastModified: '2013-06-16T23:19:07.000Z', ETag: '"9f7ee9d2fdce5f460b2c42444edd6efc"', Size: '167164', Owner: [Object], StorageClass: 'STANDARD' } ], '@': { xmlns: { xmlns: 'http://s3.amazonaws.com/doc/2006-03-01/' } } }

The "{Contents: null, Key: '[file path/name]'}" is my attempt to interpret the API instructions referenced above.

As you can see, it just enumerates the contents of the bucket, but I need the API call to check for the existence of a specific file.

Can anyone help? I'm fairly new to all this.

Problem courtesy of: metalaureate

Solution

You have to do it the HTTP way with a HEAD request. A file that does not exist triggers a 404 error. Here's a simple implementation:

var s3 = require('aws2js').load('s3', process.env.AWS_ACCEESS_KEY_ID, process.env.AWS_SECRET_ACCESS_KEY)

s3.setBucket(process.env.AWS2JS_S3_BUCKET)

s3.head(process.argv[2], function (err, res) {
    if (err) {
        console.log(err)
        return
    }
    console.log(res)
})

For testing purposes, invoke it with:

node s3-check.js file/path.foo

Examples:

node s3-check.js foo1.png
{ 'x-amz-id-2': 'BU8rLC35oZdNLh4TkE9Y5+czR5r9hg7kl/EbhkxUF+cA94F41knI2YNs/YG1acQg',
  'x-amz-request-id': '7714B364EC1A27B2',
  date: 'Mon, 17 Jun 2013 06:42:52 GMT',
  'last-modified': 'Tue, 28 May 2013 13:18:12 GMT',
  etag: '"2830931876c37237ae98458a99e86d85"',
  'accept-ranges': 'bytes',
  'content-type': 'image/png',
  'content-length': '1165',
  server: 'AmazonS3' }

node s3-check.js foo0.png
{ [Error: API error with HTTP Code: 404]
  headers:
   { 'x-amz-request-id': '96841B9C0BC5E66D',
     'x-amz-id-2': 'ZMZH9bkrR6nhDkWK1hM+qm0dlzBOYhOZhVyT3nKlMuZgPag//5EhfBuAHZq+9ZRm',
     'content-type': 'application/xml',
     'transfer-encoding': 'chunked',
     date: 'Mon, 17 Jun 2013 06:43:53 GMT',
     server: 'AmazonS3' },
  code: 404 }
Solution courtesy of: SaltwaterC

Discussion

The existing answer is a few years old. There's a better way to do this. By using the aws-sdk module, you're provided a nice API through which to do this.

aws-sdk has a function called headObject to look at a file's metadata. Here's example usage. I wrote it in coffeescript using promises.

Promise = require 'bluebird'
AWS = require 'aws-sdk'
AWS.config.accessKeyId = Config.Credentials.AWS.accessKeyId
AWS.config.secretAccessKey = Config.Credentials.AWS.secretAccessKey

S3 = new AWS.S3
Promise.promisifyAll S3

S3.headObjectAsync
    Bucket: 'bucketname'
    Key: 'file.txt'
.then (result)->
    console.log 'success'
    console.log result # this is an metadata object for the file, as seen below:
    ###
    { AcceptRanges: 'bytes',
    LastModified: 'Mon, 16 Jan 2017 07:30:19 GMT',
    ContentLength: '6',
    ETag: '"b1946ac92492d2347c6235b4d2611184"',
    ContentType: 'text/plain',
    Metadata: {} }
    ###
.catch (error)->
    console.log 'failure'
    console.log error.statusCode # 404
Discussion courtesy of: HasFiveVowels

This recipe can be found in it's original form on Stack Over Flow.