jcberquist / aws-cfml

Lucee/ColdFusion library for interacting with AWS API's
MIT License
72 stars 53 forks source link

Download is corrupted #32

Closed tnamorf closed 4 years ago

tnamorf commented 4 years ago

Hey John,

I'm sure this is not an issue so much as something I'm doing wrong & I would really appreciate any insight you could provide as I'm having the devil of a time trying to get to the bottom of it. I'm using your excellent library inside a function thusly:

        try {

            response = aws.s3.getObject(
                Bucket = "#arguments.s3_bucket#",
                ObjectKey = "#arguments.s3_file_key#"
                );

            if (response.statusCode eq 200) {

                file_content = response.rawData;
                file_disp = response.responseHeaders['Content-Disposition'];
                file_type = response.responseHeaders['Content-Type'];
                file_length = response.responseHeaders['Content-Length'];

                cfheader(name="Content-Disposition" value=file_disp);
                cfheader(name="Content-Type" value=file_type);
                cfheader(name="Content-Length" value=file_length);
                cfcontent(file=file_content reset="true" type=file_type);

                return "Success";

            } else {
                return "S3 Download Error!";
            }

        } catch (any e) {
            return "Error: #e.message#";
        }

Authentication has already been done. Files look OK on S3 (They've been uploaded using your library) & will download successfully through the console.

It looks like it works OK & "success" is returned with the downloaded file - but the file content is corrupted (as in I can't open it). Do you have any idea why this might be? Do I need to process the rawData somehow? Any help would be very gratefully received. TIA

jcberquist commented 4 years ago

I am not sure if it will solve your issue, but when you are using cfcontent with binary data and not a filename you need to use cfcontent(variable=file_content) and not cfcontent(file=file_content). Note: the docs (https://cfdocs.org/cfcontent) indicate that you are supposed to provide the name of the variable as a string, so file_content might need to be quoted (e.g. cfcontent(variable="file_content").

tnamorf commented 4 years ago

That fixed it! Thanks John, I really appreciate you taking the time to help. I'm back to CFML after many years away and still making rookie mistakes it seems!

tnamorf commented 4 years ago

Hi John, I thought we had everything working correctly - but Office X documents come through with the rawData content looking like the attachment. All other files are fine. Is this something you've seen or know anything about? 1

jcberquist commented 4 years ago

Hi @tnamorf - you can see that Lucee has decided the downloaded file content is a string and attempted to decode it as such. If you look at the docs for cfhttp you will see an option for getasbinary. The default setting is auto where Lucee will decide if the content is binary or text. So in this case Lucee has decided that your file content is a string - I am not sure what makes it decide that. Can I ask what the content type of such documents is?

If you call .getBytes() on the rawData in this situation, e.g.:

file_content = response.rawData.getBytes();  

does that help?

tnamorf commented 4 years ago

Hi John, thanks that makes sense & I feel is definitely a step closer, however I still can't make it work. Content type is Open XML - so application/vnd.openxmlformats-officedocument.wordprocessingml.document, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, or application/vnd.openxmlformats-officedocument.presentationml.presentation.

I need to do some more testing but now I'm now thinking it might be better to use generatePresignedURL anyway - if you don't mind I will leave this open & report back with my findings as it may help someone else.

tnamorf commented 4 years ago

And... generatePresignedURLworks perfectly, problem solved :) In the hope this may help someone else, here's the code I'm using to generate a link to the S3 file:

            try {

                response = aws.s3.generatePresignedURL(
                    Bucket = "#arguments.s3_bucket#",
                    ObjectKey = "#arguments.s3_file_key#",
                    Expires = "#arguments.url_expires#"
                    );

                return response;

            } catch (any e) {
                return "Error: #e.message#";
            }

Thanks again for help John!