How do I force download a file?

Sep 22, 2008 at 2:18 AM
Hi all,

I'm moving our music download site to use S3, so I need to enable customers to download MP3 files after they've paid for them.

Using the ThreeSharp.Wrapper GetURL method, I can successfully get an MP3 from an S3 bucket to play in the browser, but I can't figure out how to allow the customer to download the file to a path they specify on their computer. I'm not sure if this is the best method to use - I also tried the GetFileObject method but that doesn't give the customer an option as to where they want to download the file.

Would really appreciate any help as I'm on a tight deadline (aren't we all!).

Thanks a lot,
Chris
Sep 22, 2008 at 9:45 PM
I don't know how to force a download directly from the browser (maybe wrap the request in javascript?) but with a small modification to the ThreeSharpQuery.ConfigureWebRequest method you can do this from an application. Would an application be OK?

--David Morris
Sep 22, 2008 at 11:18 PM
Hi David, thanks for the reply.

It has to be done from the browser as this is for an ecommerce music download site.

I've a feeling it'll have to be done by capturing the stream from the GetFileObject method or something similar, so I'm hoping to find someone who's already done this as I haven't been able to figure it out yet.

Thanks,
Chris
Sep 24, 2008 at 10:58 AM
Hi Chris

You need to change the Mimetype of the files. The 2 'tags/attributes' you need to set are 'Content-Dispostion: attachment' and 'Content-Type: application/octet-stream'. That will force the browser to download rather than open the file.

You normally set these when you upload a file to amazon although you can change them on an already uploaded file by copying the file to itself. You can use CopyObject in the Threesharpwrapper to do this although you need to change the ObjectCopyRequest class. See below for an overload to the original class. The destination bucket and key are set to the same values as the source. The metadata-directive header tells amazon to replace the metadata on the file - the example below adds the Content-Type and Content-Disposition data.
public ObjectCopyRequest(String sourceBucketName, String sourceKey)
        {
            this.Method = "PUT";

            this.BucketName = sourceBucketName;
            this.Key = sourceKey;
            this.contentType = "application/octet-stream";

            this.headers.Add("x-amz-copy-source", sourceBucketName + "/" + sourceKey);
            this.headers.Add("x-amz-metadata-directive", "REPLACE");

            this.headers.Add("Content-Disposition", "attachment");

        }

See the CopyObject section in the amazon docs for more information

Note: as this is a copy operation it can take quite a long time on large files so you probably cant do it on the fly.

Also, it might be easier to use some software such as bucketexplorer to change these attributes.

Hope that helps
Geoff
 

Oct 28, 2008 at 10:16 AM
Thanks Geoff,

Sorry for the late reply, I went away on holidays and have only just returned.

I have Bucket Explorer, so I've changed the metadata for some test files to include both Content-Disposition and Content-Type, but they still don't bring up the standard "Save file as" window until the entire file has downloaded in the background.

This is the code I have so far:

Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load

        LoadAmazonS3Keys()

        Dim wrapper As New ThreeSharpWrapper(PublicKey, SecretKey)
        Dim url As String = wrapper.GetUrl(myBucket, "test.mp3")

        Response.ClearHeaders()
        Response.ClearContent()
        Response.ContentType = "audio/mpeg3"
        Response.AppendHeader("Content-Disposition", "attachment; filename=testFromAmazon.mp3")

        Dim wc As New WebClient
        Dim buffer As Byte() = wc.DownloadData(url)
        Response.BinaryWrite(buffer)

End Sub

I need to give some feedback to the customer so they know that the file is downloading. Any help much appreciated.

Thanks,
Chris
Oct 28, 2008 at 10:56 AM
Hi Chris

Just do a redirect using the generated url. eg:
LoadAmazonS3Keys()
Dim wrapper As New ThreeSharpWrapper(PublicKey, SecretKey)
Dim url As String = wrapper.GetUrl(myBucket, "test.mp3")
Response.Redirect(url)
or using javascript from the client-side:
location.href='url to file';
Both methods should force the browser to download the files.

Geoff



Oct 28, 2008 at 12:19 PM
Hi Geoff, thanks for the help so far, much appreciated.

That does get it working, but then I lose the ability to rename the file when the customer downloads it.

i.e. in S3 the files are named by ID numbers, but I need to rename them to the actual MP3 track title when the customer buys it.

Any idea how I can do that?

Thanks,
Chris
Oct 28, 2008 at 12:51 PM
Edited Oct 28, 2008 at 1:04 PM

Chris

Now you have me stumped!!! I am not sure how to achieve that. Sorry.

Also, your current method somewhat defeats the point of using s3 to store your files as every download has to go through your web server.

Actually you could try this.
  1. Copy the file to a new amazon location with a new friendly filename.
  2. Generate the url for the new file
  3. Do a cleanup operation to remove these 'temp' files at a later time once the download is completed.
File copies within s3 seem to be pretty quick so this might work if your files are not too big.

Hope that helps
Geoff



Oct 28, 2008 at 12:59 PM
No problem, thanks for trying. I'll keep plugging away, and if I can't find another way to do it then I may have to try your suggestion of copying the file to a new location.

Just one last thing, what did you mean by "Also, your current method somewhat defeats the point of using s3 to store your files as every download has to go through your web server."? Do you mean the method I originally posted or the one we ended up with (i.e using a redirect)?

Thanks,
Chri
Oct 28, 2008 at 1:03 PM
Your original method.

The redirect method means your user is downloading directly from s3.

Oct 28, 2008 at 1:45 PM
Ok, I see. I guess you mean that we'll still be using our web server's bandwidth in addition to S3's, which is true. We're primarily using S3 for storage reasons, but I take your point.

Hopefully someone else can help out with the original issue and also suggest a way around this.

Thanks again,
Chris