Download File from S3 bucket

@patrick I have a question about your Server Action for S3 Download file.

I’m converting my website from PHP to NodeJS and I’m not getting the Download Progress Bar to work with Node and S3 Download file.

Is it possible that there is something missing in the code for NodeJS to work with the Progress Bar? In PHP it does work without problems.

After looking at your code I found out, that there are “obviously” a lot of differences between the code for Node and PHP:

PHP:

public function downloadFile($options) {
        option_require($options, 'provider');
        option_require($options, 'bucket');
        option_require($options, 'key');

        $options = $this->app->parseObject($options);

        $s3 = $this->getClient($options->provider);

        if (!$s3->doesObjectExist($options->bucket, $options->key)) {
            $this->app->response->end(404);
        }

        $result = $s3->getObject([
            'Bucket' => $options->bucket,
            'Key' => $options->key
        ]);

        header('Content-Type: ' . $result['ContentType']);
        header('Content-Disposition: attachment; filename="' . basename($options->key) . '"');
        header('Content-Length: ' . $result['ContentLength']);

        exit($result['Body']);
    }

Node:

downloadFile: async function(options) {
        const provider = this.parseRequired(options.provider, 'string', 's3.downloadFile: profider is required.');
        const Bucket = this.parseRequired(options.bucket, 'string', 's3.downloadFile: bucket is required.');
        const Key = this.parseRequired(options.key, 'string', 's3.downloadFile: key is required.');
        const s3 = this.getS3Provider(provider);

        if (!s3) throw new Error(`S3 provider "${provider}" doesn't exist.`);

        this.res.attachment(basename(Key));
        s3.getObject({ Bucket, Key }).createReadStream().pipe(this.res);
        this.noOutput = true;
    }

@patrick did you already have the time to check this? I still can’t get the progress bar to work with node and s3 Download file. I have no problems on my PHP site.

Thank you for checking as I would like to finalize my PHP/Node Conversion :slight_smile:

I think the node version is probably missing the ContentLength header, perhaps you could check that in the network tab of devtools. When the ContentLength header is missing it can’t calculate the percentage downloaded.

You are correct, Content-Length is missing:

Is there something you can change to make it work?

@patrick did you already have the time to look at this?

@patrick is there something I can do?

bump :slight_smile:

Not sure if it will work, but try this update.

s3.zip (1.2 KB)

Thank you! Sadly this breaks the download functionality.

I get this in devtools:

What I added was:

const data = s3.headObject({ Bucket, Key }).promise();

this.res.set('Content-Length', data.ContentLength);

https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/S3.html#headObject-property

I currently have a lot of work so can’t spend to much time on debugging. You can check what the data is that was returned by the headObject function, just return it in the downloadFile function, enable output on the action and you should see it in the json response. It probably is something simple like perhaps the case is wrong. Or maybe the S3 provider doesn’t return the content length.

If you find the solution I will make sure it will be implemented in the next update.

1 Like

Forgot the await to wait for the promise to finish, here the update.

s3.zip (1.2 KB)

1 Like

Thanks a lot for your updated file! I can confirm that the progress bar does work now.

I am running into the exact same issues.

With Teodor’s instructions I can get it to download, but just like @MH2ag I get 2 issues:

  1. It doesn’t download directly (it opens a new tab)
  2. It throws a 403 error

Now the last thing said is:

So I’m assuming this would fix my issues. Yet I’m not sure how the intended usage is…

In the network tab I can see that I get a 200 status code:

The preview shows the binary file: image

However, it doesn’t download or open in a new tab.
What it does is trigger the dmx-on:error="notifies1.warning(' '+orders_download_invoice.lastError.response)" code.

Which is strange, as the response is 200.

Using the console I can find why it triggers the error message code:

I also get a 403, but the file just downloads so I don’t really see it as a problem. Did you set content disposition to attachment?

I will check my configuration and give you some info later. It does work for me.

You should not call it with a serverconnect component when it is a download, call the action with a download component or a link tag with download attribute.

Hi Patrick, I have a serverconnect that signs the download url and on succes I use the download component to download the file with the signed url. Is that the correct way? For me it works, but I get this error:

403 error means that download is not allowed, the download url is probably not correctly signed.

Well the weird thing is that it does download the file…
So some things I tried: if I disable the step of signing the download url in the server action, the download component just downloads a html document called ‘download’. Not the actual s3 file.

If I enable the signdownloadurl step, it signs the url and downloads the file AND comes with a 403 error. If I copy the url like so:


And just access that directlty from within chrome, I don’t get the 403. It just downloads the file.

Have you any idea what is up here?

I’m using the “download file” server action:

And then I use the route:

On the frontend I use download component with a button:

1 Like

And this works 100% without 403?
I don’t really undestand what you are doing with the route.