Tomas Barreiro
Tomas Barreiro

Reputation: 305

AWS SDK Crashing Node app if item doesn't exist in s3

I'm trying to use an S3 to store my application images and files. The thing is that even though I am using try...catch, if I try to fetch a file that doesn't exist, it crashes because access was denied. This is the function I use to get the files:

export const getImage = (req: Request, res: Response, next: NextFunction) => {
    try{
        const key = req.query.key;

        if(!key) throw new HttpException(403, 'Hubo un error al cargar la imagen');
        
        const readStream = getFileStream(key);
        if(readStream) {
            readStream.pipe(res);
        } else {
            throw new HttpException(403, 'Hubo un error al cargar la imagen');
        }
    }catch(err){
        next(err);
    }
}

And the getFileStream function is:

export const getFileStream = (fileKey: string) => {
    const downloadParams = {
        Key: fileKey,
        Bucket: bucketName
    }

    return s3.getObject(downloadParams).createReadStream();
}

The error is:

\node_modules\aws-sdk\lib\services\s3.js:718
      resp.error = AWS.util.error(new Error(), {
                                  ^
AccessDenied: Access Denied

What gets node to say:

npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! [email protected] start: `ts-node src/app.ts`
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the [email protected] start script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

I tried adding try...catch in the getFileStream function but that didn't work either.


Nir Alfasi suggestion:

turned get Image into an async function and tried doing this:

try{
    const readStream = await getFileStream(key);
    if(readStream) {
        readStream.pipe(res);
    }
} catch(err) { console.log(err)}

Also tried not adding a second try...catch and just using the one that is already there but didn't work either. Same error, Access Denied and crashes.

D:\Personal\Yo\Paideia\ed\TypeScript\API\node_modules\aws-sdk\lib\services\s3.js:718
      resp.error = AWS.util.error(new Error(), {
                                  ^
AccessDenied: Access Denied
    at Request.extractError (D:\Personal\Yo\Paideia\ed\TypeScript\API\node_modules\aws-sdk\lib\services\s3.js:718:35)
    at Request.callListeners (D:\Personal\Yo\Paideia\ed\TypeScript\API\node_modules\aws-sdk\lib\sequential_executor.js:106:20)
    at Request.emit (D:\Personal\Yo\Paideia\ed\TypeScript\API\node_modules\aws-sdk\lib\sequential_executor.js:78:10)
    at Request.emit (D:\Personal\Yo\Paideia\ed\TypeScript\API\node_modules\aws-sdk\lib\request.js:688:14)
    at Request.transition (D:\Personal\Yo\Paideia\ed\TypeScript\API\node_modules\aws-sdk\lib\request.js:22:10)
    at AcceptorStateMachine.runTo (D:\Personal\Yo\Paideia\ed\TypeScript\API\node_modules\aws-sdk\lib\state_machine.js:14:12)
    at D:\Personal\Yo\Paideia\ed\TypeScript\API\node_modules\aws-sdk\lib\state_machine.js:26:10
    at Request.<anonymous> (D:\Personal\Yo\Paideia\ed\TypeScript\API\node_modules\aws-sdk\lib\request.js:38:9)
    at Request.<anonymous> (D:\Personal\Yo\Paideia\ed\TypeScript\API\node_modules\aws-sdk\lib\request.js:690:12)
    at Request.callListeners (D:\Personal\Yo\Paideia\ed\TypeScript\API\node_modules\aws-sdk\lib\sequential_executor.js:116:18)
npm ERR! errno 1
npm ERR! [email protected] start: `ts-node src/app.ts`
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the [email protected] start script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

Upvotes: 2

Views: 2203

Answers (2)

Tomas Barreiro
Tomas Barreiro

Reputation: 305

Nir Alfasi helped me and I found a working answer. The 403 error gets thrown because I didn't have permissions to access a list of existing bucket items, so, if I granted my self that permission I would get a 404. Now, to handle the error I had to add an event listener:

const readStream = getFileStream(key);
if(readStream) {
    readStream.createReadStream().on('error', e => {
        console.log(e);
    }).pipe(res);
}

And changed getFileStream to only get the object:

export const getFileStream = (fileKey: string) => {
    const downloadParams = {
        Key: fileKey,
        Bucket: bucketName
    }

    return s3.getObject(downloadParams);
}

Answer found here

Upvotes: 1

Nir Alfasi
Nir Alfasi

Reputation: 53565

createReadStream() returns a promise, which means that we can await on it and also surround it with try/catch to handle errors.

Try changing:

const readStream = getFileStream(key);

to:

try {
    const readStream = await getFileStream(key);
} catch (e) {
    if (e.statusCode === 404) {
        // handle item doesn't exist
        // ...
    }
}

Upvotes: 2

Related Questions