Android and the HTTP download file headers

I lately had to create a complex download repository for a customer. Multiple files could be selected and were compressed on the fly into a single ZIP file before being transfered to the client. After everything worked fine with IE, FF and Chrome, I checked with Android 2.1 and the download failed. Here is why, and how to cure it…

I faced a whole bunch of problems like the browser not reacting at all, the browsers reporting that “The content being downloaded is not supported by the phone”, the download taking place but the content of the file being empty or some HTML garbage, or the browser downloading the file but ignoring my file name and trying to save the file under the name of the script that generated it. (I guess not many users would know how to handle a ZIP file that is downloaded with the name “index.php” or similar. *sigh*)

The cause

Actually there are two show stoppers at work here (see section “GET, POST, REST” below for the second pitfall, connected to POST requests).

The first problem I found is that the Android browsers are extremely picky about two of the usual HTTP download headers. As I experienced this behavior with several browsers on a Motorola Defy running Android 2.1 (Eclair) but not after an update to 2.2 (Froyo), I conclude that this is an Android problem and not a browser issue.

Here are some header lines that did not work on Android 2.1 – just to show how NOT to do it.

ATTENTION:
If you use any of the lines below your download will probably NOT WORK on Android 2.1.

Content-Type: application/force-download
Content-Disposition: attachment; filename=MyFileName.ZIP
Content-Disposition: attachment; filename="MyFileName.zip"
Content-Disposition: attachment; filename="MyFileName.ZIP";

And the solution

To make it work in Android’s stock browser and in Dolphin browser you have to use:

Content-Type: application/octet-stream
Content-Disposition: attachment; filename="MyFileName.ZIP"

Please mind the double-quotes around the filename parameter and the uppercase .ZIP extension of the filename. Both are absolutely necessary for ZIP files on Android 2.1 to download correctly! From my web research it looks like it is possible that the uppercase extension is only required for ZIP files and only for Android versions up to 2.1. I am not sure of that and did not do any further testing. If you create other downloadable file types (e.g. PDF), please test yourself and leave a comment below.

[UPDATE 20120530, thanks to Rachel for reporting this]:
Please also make sure that there are no trailing semicolons behind any of these headers. Otherwise especially Content-Disposition will not be parsed properly by the Android stock browser (up to and including Android 4, Ice Cream Sandwich). Example:

Content-Disposition: attachment; filename="MyFileName.ZIP";

…will not work correctly because of the semicolon at the end. Such trailing semicolons are reportedly generated by some eCommerce packages that provide download links. With such a header the download will still happen, but the desired filename will be lost. Android will replace it with the name of the script that generates the download (e.g. index.php), or with the generic name downloadfile.bin (if the URL of the script contains additional parameters like in index.php?id=321).

How to create the headers  [UPDATE 20110623]

How you create those headers largely depends on your scenario and your programming language.
In PHP it would look like this:

header("Content-Type: application/octet-stream");
header("Content-Disposition: attachment; filename="$zipBaseName.ZIP"");

If you are dealing with static downloadable files on an Apache webspace and you only want to link to them from some web page, you have to inform Apache to use the right headers, e.g. by appropriate statements in an .htaccess file somewhere up in the path of your downloadable file. If we would not have to include the filename in the Content-Disposition header this would be simple:

<Files *.ZIP>
  ForceType application/octet-stream
  Header set Content-disposition "attachment"
</Files>

But to include the filename on the fly we have to do some more magic:

<Files *.ZIP>
  ForceType application/octet-stream
  SetEnvIf Request_URI "^.*/([^/]*)$" MYFILENAME=$1
  Header set Content-disposition "attachment; filename="%{MYFILENAME}e""
  UnsetEnv MYFILENAME
</Files>

And don’t forget to store your ZIP files with uppercase extensions, as Apache will not change your lyrics for you!

Fortunately the above headers will also work for all major desktop browsers, so they are a good general solution. And let’s just hope that future Android versions will be less pesky.

Testing Different Headers [UPDATE 20111025]

Here is a little script to quickly test the reaction of your Android gadget with different header combinations:


Content-Type: application/octet-stream
Content-Disposition: attachment; filename=”digiblog.ZIP”
TRY DOWNLOAD (Should work on all versions of Android!)


Content-Type: application/octet-stream
Content-Disposition: attachment; filename=”digiblog.zip”
TRY DOWNLOAD (May NOT work on older versions of Android – lowercase filename extension!)


Content-Type: application/octet-stream
Content-Disposition: attachment; filename=digiblog.ZIP
TRY DOWNLOAD (May NOT work on older versions of Android – filename not quoted!)


Content-Type: application/force-download
Content-Disposition: attachment; filename=”digiblog.ZIP”
TRY DOWNLOAD (May NOT work on older versions of Android – wrong Content-Type!)


To create your own tests you may use this URL syntax:
http://www.digilog.de/pub/blog/android_download_header_test.php/param1/param2/param3/…/
where paramX may be any of the following strings (headers are sent in order of parameters):

ctaos = Content-Type: application/octet-stream
ctafd =  Content-Type: application/force-download
cdafqXXX  (XXX may be GIF, PDF, ZIP, gif, pdf, zip)
= Content-Disposition: attachment; filename=”digiblog.XXX”
cdafXXX  (XXX may be GIF, PDF, ZIP, gif, pdf, zip)
= Content-Disposition: attachment; filename=digiblog.XXX

Examples:
the four preconfigured tests above use these links…
– http://www.digilog.de/pub/blog/android_download_header_test.php/ctaos/cdafqZIP/
– http://www.digilog.de/pub/blog/android_download_header_test.php/ctaos/cdafqzip/
– http://www.digilog.de/pub/blog/android_download_header_test.php/ctaos/cdafZIP/
– http://www.digilog.de/pub/blog/android_download_header_test.php/ctafd/cdafqZIP/

When you use this testing feature please leave a comment and tell us about your Android version and your results! Also, if you need additional headers for your testing, drop a comment and I will add them. 

GET, POST, REST  [UPDATE 20120208]

Over the course of comments to this article, a second possible pitfall showed up its ugly head. While all my testing examples carry their control parameters encoded into their URLs, many applications that you write out there will not. They will often use GET or POST requests to transfer data that is needed to create the downloadable document on the fly. If you are using POST for this transfer, you’re in trouble:

As several commenters (special thanks to Juan and Ralf) have pointed out, there is a problem with POST requests on many Android devices. It looks like POST does work as expected when the result of the POST request is again a web page, but the POST data gets completely lost when the request initiates a file download.

<speculation> A possible reason for this weird behavior could be that Android (or the browser in use) forwards the URL for the file download to some helper application (Android Download Manager?) that executes the actual download. In this process the POST data is either not transferred, or gets lost, or maybe the complete session context incl. any POST data gets lost and the bare POST URL is used to request the file. This results in a file download with a zero file size, or incorrect file content, or even some HTML garbage in the file (if the server application does return a page content when the POST parameters are missing).
But careful: I have to admit that my knowledge of the exact HTTP handshake in the process of a file download is rather limited. So forgive me if my speculations are bending or breaking some fundamental laws of HTTP physics. </speculation>

As Ralf has pointed out in his comment, the problem exists at least up to Android 3.2.1 and is heavily browser dependant. POSTed downloads seem to work on Opera and Firefox but fail on Dolphin and the Android stock browser.

To avoid this trap you have to use GET requests or RESTful URLs (keeping all needed data for an HTTP request within the URL itself, as I did in my examples above)!

More research on the POST problem can be found in the detailed answer by Steve Payne to this StackOverflow question.

[SUB-UPDATE 20120209]: The above speculation seems to be dead on. I found issues 1780 and 3948 in the Android forum, which describe the inability of the stock browser and the Android Download Manager to correctly handle downloads that are initiated through POST requests. Both reports have been acknowledged by forum moderators, but labelled as “enhancements” instead of as bugs. So they are probably handled as feature requests. 1780 is over 3 years old, starred by +100 visitors and there is still no indication that it has been resolved, not even in the latest Android version (being 4.0.4 at the time of this writing).

Authentication?  [UPDATE 20120223]

Another defect of the Android Download Manager is its inability to handle basic authentication. If you use authentication to control access to your download script, your downloads will fail with all browsers that make use of the ADM (e.g. Android stock browser and Dolphin). The reason is surely the same as for the POST issue mentioned above: The ADM receives the download URL from the browser, but any further information (e.g. authentication session, POST parameters) are simply not transferred. So the ADM gets blocked out by the authentication mechanism, even if the browser has successfully authenticated.

There are multiple strategies to solve the authentication problem:

Depending on how much you need to protect (the whole web page content or only the download file content) you could remove the basic auth and implement a normal password form field in the page that initiates the download. The password check would then be done by the script that receives the form and delivers the download.

Another option is to check the user agent of the application requesting the download and unblocking all access that has a UA of “AndroidDownloadManager”. This can be done on server level (e.g. using .htaccess rewrite rules on Apache). But this is by no means a strong protection. Anybody who is aware of the details can fake the UA and access the download with any normal desktop browser. The pro is that this allows you to keep basic auth in place and protect a whole section of your website, including web pages and files. The con is that you should only use this technique if a data leak would not get you fired.

Help to make it better

One important hint about a part of the problem (quoting the filename) came from this Android bug report:

http://code.google.com/p/android/issues/detail?id=11422

The POST part of the problem (see section “GET, POST, REST” above) is described in this bug report:

http://code.google.com/p/android/issues/detail?id=1780

The authentication problem (see section “Authentication?” above) is described in this bug report (kudos to Vladimir for finding it):

http://code.google.com/p/android/issues/detail?id=1353

If you have also been struggling with the problems mentioned in this article and if you found a solution here, I propose that you fuel the above bug reports (especially the second one) by clicking the vote star at the bottom of the reporting page (above the comment field).

Dieser Beitrag wurde unter Android, Bug, Configuration, HTML, PHP, Web Browser veröffentlicht. Setze ein Lesezeichen auf den Permalink.

98 Antworten auf Android and the HTTP download file headers

  1. Drew sagt:

    Do you have any experience with the honeycomb browser that comes with Android Tablets? I have a custom ASP.NET site and am trying to download a pdf file from it on a XOOM. However, it downloads the html of the page, instead of the file I’m trying to get. I used your suggestion but still didn’t have any luck with it. Let me know if you’ve experienced anything like this or if you have any suggestions. Thanks.

  2. Jörg Wagner sagt:

    Hi Drew,
    nope, I didn’t get my hands on a honeycomb tablet yet.

    Here are the steps that I recommend:
    Check, whether the download really contains the HTML of your web page. It is possible that the content is your PDF but the name is the filename part of the delivering page.
    If the content is really HTML then you probably have a server side problem or a flaw in your download logic which obviously accesses the wrong URL (otherwise it would not get the HTML as a response from the server). To exactly check the URL that is requested, use Firefox with the add-on “Live HTTP headers”.
    If you in fact get the PDF under wrong name, you will have to find out which header makes the XOOM use the wrong name. Again use Live HTTP headers to exactly monitor the headers that are sent from the server to the browser. Then change your code to reduce the headers to the absolute minimum and experiment with variations of the Content-Type and Content-Disposition headers.
    Good luck and tell us your findings!

  3. Drew sagt:

    I have verified that the download logic is correct by going to my site in IE, Firefox, Chrome, Safari, and on an iPad and downloading the file. Thanks for the suggestion on Live HTTP headers. Here’s what I currently have:

    Content-Type: application/octet-stream
    Content-Disposition: attachment; filename=”Agent Status Changes_04-26-2011 16.47.20_266.PDF”

    Also, if I omit both of these things it displays the bytes of the pdf in the browser, but once I put either of them in it downloads the html of the page (which I found out by opening my download in a text editor). I’ve also verified that I can open other pdf files just fine on the XOOM. I’ll keep playing with the type and disposition values to see if I can get anything to work.

  4. Jörg Wagner sagt:

    You are at a similar point as I have been some weeks ago. This is definitely detective work and the solution does not come easy. Some more ideas that might help:
    Can you open ANY PDF through clicking a simple link in a website?
    Could it be that the filename with blanks and additional dots is the problem? Try a very short name.
    To really get to the bottom of this I recommend finding a way to monitor and analyze the communication between the server and the Android browser (much like Live HTTP headers does for Firefox). If you have access to your Apache access logs this would probably be the first place to look at. To see the whole communication including all HTTP headers and responses you can use a proxy software between your Android and your webserver. There are several solutions out there – try googling for “http monitor proxy”.
    Good luck.

  5. Drew sagt:

    Thanks for the suggestions. Here’s something weird….I extracted my code to download the file to a little test site. I noticed that if I download the file on Page_Load it works and I get the file correctly. However, if I download the file after a postback (after clicking a download link) it downloads the page html instead. So, if I add a second page which just does the download and my link redirects to that page it works, but that seems a bit hacky. Does this kind of behavior seem to ring a bell with anything you’ve seen (that the download works on Page_Load but not after a postback)?

  6. Jörg Wagner sagt:

    Just an educated guess: Sounds like a caching problem.

    If your downloading from a second page the browser (and any intermediate proxy) is forced to poll the data from the new URL. If you request the download from the same URL instead, a badly configured proxy might ignore the additional post parameters and just deliver the cached HTML page from the first request.
    Try additional headers to prevent proxy caching:

    Pragma: no-cache
    Expires: -1
    Cache-Control: must-revalidate, no-cache, post-check=0, pre-check=0

    You could also take a look at the headers that are sent with the original HTML page (these are the ones a proxy will likely remember and obey when the URL is addressed a second time to transfer the PDF).

  7. John sagt:

    Hi Jörg Wagner,

    I stumbled upon the exact same issue myself. I found the pre installed android browser did indeed fail to download and file with some bizarre results: http://www.phpfreaks.com/forums/index.php?topic=336930.0

    I tried installing the mobile version of FireFox and everything works fine.

    Have you had any issues with uploading a file via an android smartphone?

  8. Jörg Wagner sagt:

    Hi John,
    while FireFox might be a workaround for an individual user, it is no solution for a developer. The websites I create are expected to work flawlessly on any smartphone browser. It just should work out of the box without installing additional stuff.

    Upload:
    I did not test the upload so far. Will probably have to fight with that in my next project… ;)
    Did you experience any issues with that?

  9. John sagt:

    Hi, sorry for the hugely late reply…

    Yes there were some issues with the uploads. Uploading via firefox. Firefox renamed the file being uploaded, eg:
    file.txt to something like 15sdj0ijg52887.txt

    Although this might not normally be a problem, as i was at the time working on a small ftp management/file locking system it did cause a problem. The script would only accept the file being uploaded if it (among other things) was of the same name it was to replace. So as FF renamed the dam file to a random string it always refused the upload.

    In the end, we simply restricted the use of the download/upload management system to non-mobile browsers.

    (the main use was for 2 people to be able to fiddle with scripts on their smart phones on the trains…etc so no big loss. Still a little annoying)

  10. Hi Jörg,

    I just wanted to say thank you for this post. The 2 lines with “application/octet-stream” and entering file name specifically including Quotes fixed our issue of downloading ZIP files on Android devices.

    Cheers!

  11. Kevin sagt:

    Thanks for writing this article and the pointer in the right direction.

    After spending many hours “fiddling” with this issue we have eventually got the file to be successfully downloaded on an Archos 101 requested from an IIS web server running ASP.NET 3.5

    We found that the headers had to be added in a specific order (Content-Disposition and then Content-Type), the file name MUST be wrapped in quotes as mentioned elseware, we also had to remove underscores and hyphens, and convert the filename and extension to lower case.

    Response.Headers.Add(“Content-Disposition”, String.Format(“attachment; filename=”"{0}”"”, info.Filename.Replace(“_”, “”).Replace(“-”, “”).ToLower))
    Response.Headers.Add(“Content-Type”, “application/pdf”)

    Just our experiences.

  12. Juan sagt:

    Hi Jörg,

    First of all thanks for your article. I have followed the solution guidelines and for PDF doesn’t work in my PHP code. However the second of the four preconfigurated examples works my phone:

    - http://www.digilog.de/pub/blog/android_download_header_test.php/ctaos/cdafqpdf/

    My PHP code is:
    header(‘Content-Type: application/octet-stream’);
    header(‘Content-Disposition: attachment; filename=”sample.pdf”‘);
    readfile(‘sample.pdf’);

    but I always get a html file (sample.htm, sample-1.html,…) in the browser downloads manager which just contains a “0″.

    I have tried with “application/pdf”, “sample.PDF”, “sample”, and many more… but always without success. If I don’t set the headers I can see the PDF content (as binary characters) in the new window.

    If you could share the code of the ‘android_download_header_test.php’ script I am sure many of us will be eternally grateful with you :)

    Cheers,

  13. Jörg Wagner sagt:

    Hi Juan,
    here is the code:

    $sendFileExt = "gif";
    
    $params = explode('/', $_SERVER['PATH_INFO']);
    foreach($params as $param){
      switch($param){
        case 'ctaos':
          header("Content-Type: application/octet-stream");
          break;
        case 'ctafd':
          header("Content-Type: application/force-download");
          break;
        case 'cdafqGIF':
          header("Content-Disposition: attachment; filename="digiblog.GIF"");
          $sendFileExt = "gif";
          break;
        case 'cdafqPDF':
          header("Content-Disposition: attachment; filename="digiblog.PDF"");
          $sendFileExt = "pdf";
          break;
        case 'cdafqZIP':
          header("Content-Disposition: attachment; filename="digiblog.ZIP"");
          $sendFileExt = "zip";
          break;
        case 'cdafGIF':
          header("Content-Disposition: attachment; filename=digiblog.GIF");
          $sendFileExt = "gif";
          break;
        case 'cdafPDF':
          header("Content-Disposition: attachment; filename=digiblog.PDF");
          $sendFileExt = "pdf";
          break;
        case 'cdafZIP':
          header("Content-Disposition: attachment; filename=digiblog.ZIP");
          $sendFileExt = "zip";
          break;
        case 'cdafqgif':
          header("Content-Disposition: attachment; filename="digiblog.gif"");
          $sendFileExt = "gif";
          break;
        case 'cdafqpdf':
          header("Content-Disposition: attachment; filename="digiblog.pdf"");
          $sendFileExt = "pdf";
          break;
        case 'cdafqzip':
          header("Content-Disposition: attachment; filename="digiblog.zip"");
          $sendFileExt = "zip";
          break;
        case 'cdafgif':
          header("Content-Disposition: attachment; filename=digiblog.gif");
          $sendFileExt = "gif";
          break;
        case 'cdafpdf':
          header("Content-Disposition: attachment; filename=digiblog.pdf");
          $sendFileExt = "pdf";
          break;
        case 'cdafzip':
          header("Content-Disposition: attachment; filename=digiblog.zip");
          $sendFileExt = "zip";
          break;
      }
    }
    
    // after all headers have been sent: send the file content
    echo file_get_contents('digiblog.'.$sendFileExt);

    A boiled down version just for your needs (params ctaos and cdafqpdf) would be:

    header("Content-Type: application/octet-stream");
    header("Content-Disposition: attachment; filename="digiblog.PDF"");
    echo file_get_contents('digiblog.pdf');

    Make sure that the needed GIF, ZIP and PDF files are in the same path. You can grab them here:
    http://www.digilog.de/pub/blog/digiblog.gif,
    http://www.digilog.de/pub/blog/digiblog.zip,
    http://www.digilog.de/pub/blog/digiblog.pdf
    Cheers and good luck, Jörg.

    • Juan sagt:

      Thank you so much Jörg,

      The above snippet worked!

      I had some weird problems in my PHP script whose solution I am sure will help other developers. I composed the name of the PDF file from some form fields sent by POST method. When I showed (by echo or var_dump) the form variables into the browser as plain HTML, I could see them without any problem. However, when the content type was set to octet-stream, and I used the POST variables to compound the PDF file name, these variables disappeared magically!

      Finally just changing the form method from POST to GET the variables were available with octet-stream content-type and the script worked.

      I’d like to remind to developers that trailing spaces/lines (after ?>) of included files have to be removed when you are generating PDF files on the fly. I got this problem again, even when I knew it. Output buffering functions (ob_start, ob_end_flush,..) can also help.

      Congratulations again for this helpful article.

      Cheers,

      Juan

      • Jörg Wagner sagt:

        Hi Juan,

        this is an interesting observation! I don’t know about the inner mechanics of Android downloads, but it could be that Android browsers do not handle any downloads themselves, but hand over all requests to some kind of OS download agent by simply communicating the URL. This would kill all data that is not part of the URL (like POST vars). So you are bound to RESTful programming (keeping all needed data for an HTTP request within the URL itself).

        Glad you finally made it!
        Cheers, Jörg.

      • Ralf sagt:

        Thank you very much Juan and Jörg, your work was very helpfull for me.

        I have the same problem – mainly with PDF files. The script works with GET, not with POST. For me it’s no problem to use GET, so it’s ok.

        For your interest and as confirmation of other test results: Test on ‘Sony Tablet S’ with Android 3.2 works with POST at Firefox and Opera (also at older Android versions, any other devices etc.).
        In browser Dolphin and the standard browser (Mozilla/5.0 (Linux; U; Android 3.2.1; de-de; Sony Tablet S Build/THMAS0042) AppleWebKit/534.13 (KHTML, like Gecko) Version/4.0 Safari/534.13) only GET is working.
        The other header problems from the top of the article in this environment are not available, the 4 links “TRY DOWNLOAD” are working without problems, also http://www.digilog.de/pub/blog/android_download_header_test.php/ctaos/cdafqPDF (it’s not a surprise, Jörg wrote “May NOT work on older versions of Android”).

        Here the correct working headers catched by fiddler:

        HTTP/1.1 200 OK
        Date: Tue, 07 Feb 2012 22:07:32 GMT
        Server: Apache/2.2.0 (Linux/SUSE)
        X-Powered-By: PHP/5.1.2
        Expires: 0
        Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
        Pragma: no-cache
        Content-Length: 92179
        Content-transfer-encoding: binary
        Content-Disposition: attachment; filename=”Testdocument 1.pdf”
        Keep-Alive: timeout=15, max=92
        Connection: Keep-Alive
        Content-Type: application/pdf

        I have not provided a new solution, but maybe the info is still useful.

        Thank you again.

        Ralf

        • Jörg Wagner sagt:

          Hi Ralf,
          thanks for your additional insights. Very welcome!
          I have compiled the findings of both you and Juan into an update of the article (see new section “GET, POST, REST” above).
          Cheers, Jörg.

          • Ralf sagt:

            Hi Jörg,

            I think your “speculation” isn’t built on sand. I’ve catched the URL and HTTP_USER_AGENT entries at the server:

            POST: the url without parameters and the HTTP_USER_AGENT of the browser and the download error
            GET: the full url (what else) and the HTTP_USER_AGENT “AndroidDownloadManager” an the download success.

            The download manager is the problem, you’re right. Opera and Firefox don’t use them, POST is working.

            Now I go back to the theme ‘Android 2.1′, e.g. your solution ‘uppercase extension’.

          • Jörg Wagner sagt:

            Well, and here is the corresponding bug report in the Android issues forum:
            http://code.google.com/p/android/issues/detail?id=1780
            With the real problem slowly showing up it wasn’t hard to find.
            The bug report is THREE years old and starred by +100 people. It was acknowledged by the forum moderators after 9 months and labeled as an “enhancement”, but not as a bug. *Zzzzz…*

            The moderators have also derived a second issue report (http://code.google.com/p/android/issues/detail?id=3949) that targets the download manager: “The download manager should support downloading through POST requests.”
            This second report is also labeled an “enhancement”.

            None of these issues seems to be fixed even in the latest Android versions. At least there is no report about it.
            I just don’t get it…

            • Ralf sagt:

              Hi Jörg,

              my speculation: A reason maybe the definition of GET/POST. We don’t send a message body, we send a url and headers and want to receive a (document) file. And GET works great.

              Why we want to use POST ? Security reasons ? We don’t know ?
              I’ve not many thought about it before this problem. POST was working well … . Thank you google to wake me up ;-)

              • Jörg Wagner sagt:

                While GET and POST might seem interchangeable at first glance, there are in fact notable differences between both and also defined W3C recommendations about when to use which of the two. If you are interested I suggest a Google search for “GET vs. POST”.

                But Ralf, you are right:
                For all cases of…

                • pure information retrieval from the server (most file downloads will qualify as such)
                • with very limited amount of data transferred from client to server (just some short parameters)
                • without special side effects happening on the server (except for recording the access in the server logs)

                …a GET can definitely replace a POST.

  14. Julain Emmett Turner sagt:

    I have been struggling to download files to my phone without the aid of a computer because it appears that the majority of the types of files I need to find and download online have incorrect MIME/Content types and give the ” Download Unsuccessful” message. This is on the latest version of Gingerbread, so all phones currently do this it seems, unlike your test implies when it says “old versions.” For example, I can’t download your test 4 nor the zipped SPC files from SNESMUSIC.org. Is there a client-side solution to this so that I can download from discomfiture websites?

    • Jörg Wagner sagt:

      Hi Julian,
      the following list shows which of my four download tests worked for me on my Motorola Defy with two different Android versions and two different browsers:

                         Stock browser      Dolphin browser
      Android 2.1             1                    1
      Android 2.2          1, 2, 3            1, 2, 3, 4
      

      So it seems that the success depends on the Android version as well as the browser. Your comment suggests that it additionally depends on the specific Android implementation (created by the device manufacturer).

      Just to make sure: Are your downloads really handled by the default (stock) browser? Even if you are browsing with the default browser, you can still delegate any downloads to other applications/browsers and make this decision permanent. Choosing the wrong handler here could be the reason why your trials all fail.

      What you could try to improve your situation is using another browser. Dolphin is a good choice.

      Cheers, Jörg.

    • Jörg Wagner sagt:

      It worked for you on which version of Android?

      Basically they are doing exactly what I describe here, except for a lowercase extension (which again might not work on all Android versions) and a specific Mime type that is needed because they download APK packages that must be installed by the OS.

  15. K.Kong sagt:

    I tried everything but still could not design my web page to make a Galaxy Note (Gingerbread 2.3.5) to download a simple txt file from it. All of the following (actual Fiddler dump) works on IE on a PC, but not on the Galaxy Note.

    HTTP/1.1 200 OK
    Cache-Control: private
    Content-Type: application/octet-stream; charset=utf-8
    Server: Microsoft-IIS/7.5
    content-disposition: attachment;filename=”FERC.TXT”
    X-AspNet-Version: 2.0.50727
    X-Powered-By: ASP.NET
    Date: Tue, 17 Jan 2012 04:16:28 GMT
    Content-Length: 27357

    HTTP/1.1 200 OK
    Cache-Control: private
    Content-Type: application/txt; charset=utf-8
    Server: Microsoft-IIS/7.5
    content-disposition: attachment;filename=FERC.txt
    X-AspNet-Version: 2.0.50727
    X-Powered-By: ASP.NET
    Date: Tue, 17 Jan 2012 03:12:21 GMT
    Content-Length: 27357

    HTTP/1.1 200 OK
    Cache-Control: private
    Content-Type: application/octet-stream; charset=utf-8
    Server: Microsoft-IIS/7.5
    content-disposition: attachment;filename=FERC.txt
    X-AspNet-Version: 2.0.50727
    X-Powered-By: ASP.NET
    Date: Tue, 17 Jan 2012 03:27:01 GMT
    Content-Length: 27357

    HTTP/1.1 200 OK
    Cache-Control: private
    Pragma: no-cache
    Content-Type: application/octet-stream; charset=utf-8
    Server: Microsoft-IIS/7.5
    content-disposition: attachment;filename=”FERC.TXT”
    X-AspNet-Version: 2.0.50727
    X-Powered-By: ASP.NET
    Date: Tue, 17 Jan 2012 04:25:28 GMT
    Content-Length: 27357

    HTTP/1.1 200 OK
    Cache-Control: no-cache
    Pragma: no-cache
    Content-Type: application/octet-stream; charset=utf-8
    Expires: -1
    Server: Microsoft-IIS/7.5
    content-disposition: attachment;filename=”FERC.TXT”
    X-AspNet-Version: 2.0.50727
    X-Powered-By: ASP.NET
    Date: Tue, 17 Jan 2012 04:29:22 GMT
    Content-Length: 27357

    Sometimes it says “Cannot download. The content is not supported on this phone.” At other times it downloads the html output instead.

    Any new developments since last year?

    • K.Kong sagt:

      It’s a stock Android with stock browser.

      It says “Cannot download” if it’s application/txt. It gets the html if it’s application/octet-stream.

    • Jörg Wagner sagt:

      The first difference I see to my working examples is the encoding (charset=utf-8) appended to all of your Content-Type headers. Did you try to leave that out? AFAIK it is only valid for type text/html. It is also not needed for a binary download as the content is not interpreted at download time.
      Another detail is the all lowercase writing of Content-Disposition in all of your headers. Did you try to change that?
      If both does not help: put some demo online and post the URL. I will give it a try.

  16. K.Kong sagt:

    The encoding was put in by IIS. I do need the encoding for the text file.

    I changed to proper casing for Content-Disposition but that didn’t help.

    I have set up two test links at http://ferc.org.sg/Test.aspx.

    Thanks for helping to troubleshoot.

    • Jörg Wagner sagt:

      No, you do not need the encoding if you want to download the file. The encoding would not change the way the binary stream is downloaded and saved, it is just a sequence of bytes which is not interpreted at download time. In the saved file the encoding information from the HTTP header is lost anyway. Any program that reads the stored file has to assume an encoding or determine the encoding from the content, e.g. by analyzing special character patterns or by looking for a BOM.
      Please try to get rid of this extra in the header first and see if that helps.

      • K.Kong sagt:

        I have gotten rid of the charset parameter, but still no joy.

        My test page is updated. Do you get the same result on your Android device, ie Cannot Download for the first one and the html page for the second one?

        • Jörg Wagner sagt:

          I have tested your links on a Motorola Defy with Android 2.2.2. Both buttons result in a file download with correct name (test.txt / test.TXT), but both times the file content is the HTML code of the original page. In a normal desktop browser both buttons work correctly.
          This suggests that there is something going wrong with the form parameters that are sent back to your .NET web app. As the URLs of the original page and the form action for the download are identical (Test.aspx), the app decides which content to return along the POST parameters of the HTTP request.
          To nail down the problem I suggest that you write any incoming GET and POST parameters into a log file and compare the log when you touch the buttons on Android vs. clicking in a desktop browser.

        • Jörg Wagner sagt:

          Please also have a look at the comments of Juan and Ralf above and at my latest edit of the article (new section “GET, POST, REST” above). It looks like – depending on browser in use – any POST request data gets stripped off when a file download is initiated. This would result in the exact behavior that you describe. Changing to GET should solve this problem!

  17. Pingback: 奇話奇說 » Android and the HTTP download file headers

  18. Vladimir sagt:

    Hi,
    first, thank you very much for this awesome post, as it really solves the problem!
    I followed the instruction in this page and corrected my headers… still, I fail to download a pdf file using androids native browser (only opera succeeds).
    My current headers are (after fixing them according to your directions):

    Content-Disposition = attachment; filename=”adiReport.PDF”
    Content-Type = application/octet-stream

    can it be anything else? what do you think?

    • Vladimir sagt:

      Update: I use a GET command to retrieve the PDF:
      i.e.
      http://AdiMobileServer.zapto.org:8080/AdiRestService/resources/dao.students/reports/students

      another strage thing i noticed, is that the android default browser changes the filename according to the URL string (in this case: “students.pdf” instead of “adiReport.PDF”)

    • Jörg Wagner sagt:

      Hi Vladimir,
      how does your download fail exactly? Give some details please.
      I also recommend that you terminate your URL by a slash if it is only a path. Terminating by …reports/students actually means script “students” in path “…reports/”.
      While most Apache incarnations are configured to add the trailing slash automatically, I definitely would not count on it.
      Cheers, Jörg.

      • Vladimir sagt:

        After i press on the link, the I get the dialog asking me under what file name to save the attachment (and the file name is as expected), but when i click OK (start download) the download immediately appears as unsuccessful in the downloads screen. I tried terminating with a slash, but that wasn’t successful also. It might be worth mentioning that I also use some query parameters in the URL string:
        http://adimobileserver.zapto.org:8080/AdiRestService/resources/dao.students/reports/students?active=true&embedded=save
        this is the full string I use. it works perfectly in opera but fails both in the native browser and in dolphin. maybe it’s some kind of security issue? maybe I should add some strange security related header that android expects?

      • Jörg Wagner sagt:

        Klicking your link I see that you use basic authentication to secure the path of your files. I am quite sure that this is the problem. As pointed out in the article, some browsers do hand over the download request to the Android Download Manager. This application is surely not aware of the authentication that the browser has negotiated. Remember: The ADM is not even able to handle POST requests correctly!
        Try to disable the authentication for a moment and see if it works then.

        • Vladimir sagt:

          actually i am pretty sure its not the problem, as I do get the authentication screen and authentication succeeds. after I am authenticated, I don’t even need to log in again, as the previous session is still active. Anyway, I tried to disable the authentication and it still does not work.

        • Jörg Wagner sagt:

          Sorry Vladimir, I’m at a loss.
          The only option I can offer you is to open up access so I can give it a try from my side.

          • Vladimir sagt:

            Thank you very mush Jorg! I finally found the problem. It is as you said in the authentication… after tI gave it another try without authentication it succeeds…
            I did some searching on the subject and found the following defect record in Google’s repository:
            http://code.google.com/p/android/issues/detail?id=1353

            It’s a well known defect of the android’s download manager.
            Thank you again for all your help!
            I hope that this will at least help some more people dealing with this issue.
            Since Google are not intended to fix this (as it seems in the defect record), I guess i will have to change my authentication method just to make it work… any suggestions for a authentication method simple enough and supported by Glassfish 3 :) ?

          • Jörg Wagner sagt:

            Glad you found the culprit! I will add your findings to the original article tomorrow (being a bit stressed today).

            As for a solution:

            Depending on how much you need to protect (the whole web page content or only the download file content) you could remove the basic auth and implement a normal password form field in the page that initiates the download. The password check would then be done by the script that receives the form and delivers the download.

            Doing a quick web search I also found the proposal to check the user agent of the application requesting the download and unblocking all access that has a UA of “AndroidDownloadManager”. This can be done on server level (at least Apache can do this using .htaccess rewrite rules – not sure Glassfish can). But be warned that this is by no means a strong protection. Anybody who is aware of the details can fake the UA and access the download with a normal browser. The pro is that this allows you to keep basic auth in place and protect a whole section of your website, including web pages and files. The con is that you should only use this technique if a data leak would not get you fired. ;)

  19. Ralf sagt:

    Additional information “Android 3.2″, standard browser and Dolphin:
    In a classic ASP application I must send the header “Content-Length” (Response.AddHeader “Content-Length”, objStream.Size)!
    In PHP this header is not required, but accepted.
    (I use the “ADODB.Stream”-object to send the document content.)
    Opera and FF: The header is not required.

    • Jörg Wagner sagt:

      Hi Ralf,
      can you give a quick update what happens if you are sending from ASP without the Content-Length header?
      Thanks, Jörg.

      • Ralf sagt:

        Hi Jörg,

        if I touch the download symbol, in the case of error it’s always the same message: ” Download fehlgeschlagen” ( Download failed ?). If I use POST or incorrect headers or other bad things.

  20. Rasmus sagt:

    Hi Jörg.

    Im making a simple QR Code on the back of a buissness card, linking to a link on my website.

    The idea is that when you scan the code, the user will go to
    http://www.euro-sten.com/kontakter/“contactname.vcf”
    - and download a vCard automaticly. It works in every browser. But on android stock browser there is a problem, its just opening a normal page with the text displayed on it.

    How can i make it download automaticly? i’ve tryed your links for android and it works.
    But when i put the code in my .access file, there is no change..

    Hope you can help.
    Best Regads Rasmus.

    • Rasmus sagt:

      PS. http://www.euro-sten.com/kontakter/t.vcf – is the one im testing on.
      ** It works in Chrome BETA Mobile browser, but not the default browser.

    • Jörg Wagner sagt:

      Hi Rasmus,

      I can confirm your problem on Android 2.2.
      But I am not sure about the reason. It probably has to do with the fact that you are using IIS on Windows. A quick analyzis shows that IIS does not a send a Content-Disposition header. Here is a comparison of the headers of your server and the headers of a server that sends a working VCF file…

      Your server is sending:

      HTTP/1.1 200 OK
      Content-Type: text/x-vcard
      Content-Encoding: gzip
      Last-Modified: Wed, 14 Mar 2012 07:59:52 GMT
      Accept-Ranges: bytes
      Etag: "1734856fb81cd1:0"
      Vary: Accept-Encoding
      Server: Microsoft-IIS/7.5
      X-Powered-By: ASP.NET
      Date: Wed, 14 Mar 2012 13:19:27 GMT
      Content-Length: 267
      

      My server is sending:

      HTTP/1.1 200 OK
      Date: Wed, 14 Mar 2012 13:19:10 GMT
      Server: Apache
      Last-Modified: Wed, 05 Oct 2011 23:29:45 GMT
      Etag: "18e224b-261-4ae95954c2c40"
      Accept-Ranges: bytes
      Content-Length: 609
      Content-Disposition: attachment
      Keep-Alive: timeout=1, max=100
      Connection: Keep-Alive
      Content-Type: text/x-vcard
      

      What you can try to do:
      • Check whether your VCF file works when downloaded from a Linux server.
      • Do some research on how to add a Content-Disposition header on IIS.
      • Try to send the file from a script and add the header manually (see PHP examples above).

      An easy way to analyze the headers sent by your server is using FireFox with the Live HTTP headers add-on. The above header dumps have been logged that way.

  21. Anton sagt:

    Hi there, i have some problems with this download too. My logic is:

    selecting image part to crop and sending it like POST to ifame with target.
    in there php is parsing and doing all what its need to do, and must push that cropped image for download. Problem is with Android phone.. tested on it, no help.

    my current headers is:

    header(“Content-type: application/octet-stream”);
    header(“Content-Disposition: attachment; filename=”test.JPG”");
    echo file_get_contents($file);

    any clues ? :/ maybe its something more needed with jpg files?

    • Anton sagt:

      Oh, sorry, tested more and more, found that the problem is with Iframe. Its not giving download if sending page to iframe. Fixed with remowing “target” from form, its working in all browsers anyway :) and on phones too. Maybe someone will have the same situation. Thank you for your big article and work to help us :)

      • Hatto sagt:

        Thanks for this information – iframe really is an issue: setting target=”_top” and method=”GET” solved the problem.
        By the way: If it does not work because of method=”POST” (or wrong target) the problem occurs after (the start of ?) the transmission of the downloaded file from the server to the client, i.e. the server gets all POST params correctly but the client does not deal correctly with its (correct) answer. (Android 2.3.5 on HTC Desire HD with Telekom Deutschland Branding).

        • Jörg Wagner sagt:

          Hi Hatto,
          your assumption that the server gets all POST parameters correctly is only partially right.
          I am quite sure that for each POST download request you will find TWO requests in your logs: One with all the POST parameters coming in correctly and one without any parameters (just using the bare URL of your request but not POST headers).
          The reason is that the stock browser processes the POST request correctly, recognizes that the result is a downloadable file, hands over the request to the download manager, and the DM fires a second request with only the URL but no POST headers included.

  22. Semika sagt:

    Thans for the good post and I wish to give some help for java developers.

    I had the same issue with downloading pdf file which was sent by a servelet as an attachment.

    We can set the servelet’s response type as follows.

    String fileName = “report.pdf”;
    response.setContentType(“application/octet-stream”);
    response.setHeader(“Content-Disposition”,”attachment; filename=”" + fileName + “”");

    Specially file extension should be in lower case format. It did not work for me with upper case. I used samsung calaxy tab

    Hope this will help.

    Regards
    Semika

  23. Simeon sagt:

    Hi Jörg – this post is the stuff of legend.
    I’d love to be able to say that it’s solved it for me too, but I’m running in circles with no joy so far.

    I’m running Android 4.0.3 and can’t get a PDF download from my site working with ADM. Opera works just fine, but as you rightly say – that’s not a solution!

    Can you cast your eye over these headers and perhaps suggest where I’m going wrong?

    From my app (PHP with authorisation):

    HTTP/1.1 200 OK
    Date: Fri, 27 Apr 2012 03:19:16 GMT
    Server: Apache
    X-Powered-By: PHP/5.3.3-7+squeeze8
    Content-Disposition: attachment; filename=”summary.PDF”
    Keep-Alive: timeout=15, max=100
    Connection: Keep-Alive
    Transfer-Encoding: chunked
    Content-Type: application/octet-stream

    I also tried putting my PDF in a ‘public’ directory (not subject to any authorisation or PHP) using your .htaccess snippet, and that also fails. The header for that response:

    HTTP/1.1 200 OK
    Date: Fri, 27 Apr 2012 03:42:10 GMT
    Server: Apache
    Content-Disposition: attachment; filename=”test.PDF”
    Content-Length: 40203
    Keep-Alive: timeout=15, max=100
    Connection: Keep-Alive
    Content-Type: application/octet-stream

    The key elements you identify in your post seem to match, and I can download your tests fine. This was as close to duplicating your header as I’ve gotten so far – something there is breaking ADM though.

    Thanks!

    • Jörg Wagner sagt:

      Hi Simeon,

      > this post is the stuff of legend.

      *lol* – yes, unfortunately.
      This post alone makes up for half of the page requests of this blog…

      Your headers look perfect to me. Could you provide a link to the public (unprotected) file, so that I can have a direct look and test it from Android? I will send you an email in case you do not want to publish a link right here.

      Cheers, Jörg.

      • Simeon sagt:

        I got it working – somewhere between removing a bunch of apps from the tablet, and rebooting it, the download started to work. Possibly ADM was caching the interaction.

        The lesson: don’t be scared of a reboot every now and then, even if you’re not running Windows!

        Thanks again for this post, and your help Jörg.

        Simeon.

  24. Ankit sagt:

    Android Default Browser requires GET Request. It does not understand POST Request and hence cannot download the attachment. You can send a GET request as by sending GET request, it resolved my problem. Android browser generates a GET request on its own and sends it back to server. The response received after second request will be considered final by the browser even if GET request is sent on first time by the servlet.

    • Jörg Wagner sagt:

      Ankit,
      you basically repeat (in part) what this posting is already stating in more detail. GET is not the only option though. Anything that can be communicated from stock browser to ADM by transfering a pure URL will work (GET with querystring parameters or params mapped into the URL in any other way = REST).
      The fact that two GET requests are issued is probably due to the fact that the browser does a first request, recognizes that this is a file download (by interpreting the headers Content-Type and Content-Disposition) and then forwards the download task to the ADM which does the same GET request again to fetch the file.

  25. Rachel sagt:

    Here’s one more for you… a trailing semicolon will cause Content-Disposition not to parse properly under Browser or Chrome for Android (though will work under Firefox for Android).

    So, for instance…

    Content-Disposition: attachment; filename=”myhappytest.ZIP”

    …will work, but…

    Content-Disposition: attachment; filename=”myhappytest.ZIP”;

    …will not. Note trailing semicolon. This is a problem for some eCommerce packages and other scripts that generate download links, which can include a trailing semicolon on the Content-Disposition line.

    Mind you, the download will still happen, but without a filename. So if you just had, say, ‘test.php’ then Browser would download the binary file as ‘test.php’ instead of ‘myhappytest.ZIP’ which, well, is not ideal. And if the URL has parameters — was ‘test.php?duid=randomlonghexstring’ or something similar — then the browser just throws in the towel entirely and downloads as ‘downloadfile.bin’ as a default.

    This is still true as of Ice Cream Sandwich.

  26. Jamie sagt:

    Hi Jörg – I was very excited to see your post, however when trying your test downloads with my Android device (DROID2 running version 2.3.4 in native browser) the test download that should work for all androids fails. The download initiates, but fails immediately after without downloading anything.

    Do you know of any reason why my version of Android would not work with your tests? I’d like to understand that before I start re-working my own download headers!

    Thank you!

    • Jörg Wagner sagt:

      Hi Jamie,
      so far nobody has reported any problems with the first of the four testing links.
      And people have tested with anything from 2.1 to 4.0. So I am at a loss here.
      Please: If you should find out what is going wrong, go ahead and tell us!
      Cheers and good luck, Jörg.

      • Sii Cockerill sagt:

        Hi Jörg,

        I have a Nexus 7 and have a problem with the first Download Test link. The file downloads ‘successfully’, but doesn’t immediately offer to open the file. The PDF appears in my Downloads but when I click on it, it reports “Can’t open file”.

        I’m using Chrome. It seems to work OK in Opera Mobile (but that’s not the default or more popular browser).

        If you have any ideas, I’d really appreciate you sharing them.

        Thanks,
        Sii

  27. Jeff sagt:

    I am developing an android client for OpenEMR open source electronic medical records software. The server is implemented in PHP and kicks out long links that look like http:///openemr/controller.php?document&retrieve&patient_id=1&document_id=2&amp;

    We are having a heck of a time trying to catch these downloads. We’d like to just store them locally and open a PDF reader, but the webview just loads nothing at all. If there’s anyone who’d like to take a look at our project and help us solve this problem, our code is up at http://code.google.com/p/openemr-app/source/checkout?repo=openemr

    Its for a good cause, OpenEMR is a free open source alternative to very expensive software that’s driving up the cost of healthcare. Can you help with this bug?

  28. Jan sagt:

    Hi,
    first of all, thank you very much for looking into this, it is very much appreciated.
    I was testing the 4 test links you’re providing, but neither of them worked with my Galaxy Tab running Android 2.2 and the native browser. FF and Opera Mini however both work.
    I’m trying to implement this download functionality for one of our services and I would really like someone to test this link: https://paywithasocialpost.com/jf/ and tell me if it works. It didn’t with the mentionned setting above. The download manager is starting but never completes.
    Thanks again,
    Jan

  29. Pingback: Android default browser doesn't download a .txt file from PC localhost : Android Community - For Application Development

  30. Steve Witham sagt:

    Hi! I had different results on my Android, but solved them, so here’s my info.
    Android 2,3.7 Browser 2.3.7

    First of all, your test:
    o The first test, cdafqZIP: My phone FAILS to download the file.
    o The second test, cdafqzip: My phone SUCCEEDS.
    I think maybe they changed the list of okay extensions.
    By the way, your files are served with “Transfer-Encoding: chunked” and no Content-Length.

    I’m serving my files using a Python / WSGI script running either on a separate computer or on the phone itself. I had .txt, .jpg, .zip and .py files. None of them would download. I looked at the headers using curl -D, and I was generating the lines exactly as prescribed above.

    My response headers say HTTP/1.0 even though the request was HTTP/1.1. Maybe that has something to do with this. In my fix below I did not change the HTTP/1.0.

    I noticed that there was no Content-Length in any of the headers, but I thought maybe curl was just leaving it out, or that it didn’t matter. The download of cdafqzip worked with no Content-Length. And, all of my non-download pages displayed on browsers with no Content-Length.

    Then I noticed one of my other pages did have Content-Length and I researched a little about how that’s produced in WSGI.

    For Python people: the fix that worked for me was to have my WSGI app() function return a list with one string in it. Not a string, not a list of more than one string. All three methods produce pages my browsers can show, but only ["like this\n"] produces Content-Length.

    And that’s it. All my downloads work now.

  31. Victoria sagt:

    Hello.

    I found your article quite useful.
    However, there’s still one problem that I cannot deal with. Maybe it relates somehow to the authentication issue, but I’m not really sure, so I will post it here if you have an answer as to why that happens, some workaround… Anything would be much appreciated. :)

    What I want is – check the referer and if it’s X, then go on with the download, otherwise – load some page.
    I’m using PHP and the following does work for a direct download:

    However, using the referer check would break it for some reason:
    0) {
    header(‘Content-Disposition: attachment; filename=”my.rar”‘);
    header(“Content-Type: application/octet-stream”);
    readfile(“my.rar”);
    exit;
    ?>
    }
    The file would then show up in the downloaded files list, but have a size of 0.
    What is more, if I add an else statement – the file does not show up at all. Although it would go through the if (I checked by changing the contents of the if)

    So basically – no if – the headers work, with if or if/else – there’s some different and weird behavior.
    I was using an emulator with Android 4.0.3 if that’s of any importance.

    Best,
    Victoria

    • Victoria sagt:

      Sorry, the code got “eaten up” a bit because of the php tags I inserted:

      header(‘Content-Disposition: attachment; filename=”my.rar”‘);
      header(“Content-Type: application/octet-stream”);
      readfile(“my.rar”);
      exit;
      —> this one above is fine for direct download

      if (isset($_SERVER['HTTP_REFERER']) && strpos($_SERVER['HTTP_REFERER'],”192.168.10.100″) > 0) {
      header(‘Content-Disposition: attachment; filename=”my.rar”‘);
      header(“Content-Type: application/octet-stream”);
      readfile(“my.rar”);
      exit;
      }
      —> this one breaks in android stock browser, creates a file with size 0.
      —> if else statement is added, it wouldn’t even create a file with size 0.

  32. Mohan sagt:

    Hello There,

    I have been trying to download pdf file from asp.net website from my samsung galaxy tab but always download as .htm eventhough i have already installed pdf software. I’m able to download pdf from ipad, and all the other browsers. but only problem with android software.
    I’m using this code:

    byte[] fileBytes;
    using (var webResponse = req.GetResponse())
    {
    Stream fStream = webResponse.GetResponseStream();
    fileBytes = ReadFully(fStream);
    }
    Response.Clear();
    Context.Response.ClearHeaders();
    Context.Response.ClearContent();
    Response.BufferOutput = true;
    Response.Buffer = true;
    Response.AddHeader(“content-disposition”, “attachment; filename=” + reportName + “.PDF”);
    Response.AppendHeader(“Pragma”, “public”);
    Context.Response.ContentType = “application/PDF”;
    Context.Response.BinaryWrite(fileBytes);
    Context.ApplicationInstance.CompleteRequest();

    could you sugges any changes for this code to work in android to download PDF from asp.net website.

    regards,

    Mo

    • Hi Mo,
      In asp you have to set the contentype in the object response, not adding them to the content type header:

      Response.ContentType = "application/pdf" '<--------- this line is the key !!!
      Response.AddHeader "Pragma", "public"
      Response.AddHeader "Expires", "0"
      Response.AddHeader "Content-Type", "application/force-download"
      Response.AddHeader "Content-Type", "application/octet-stream"
      Response.AddHeader "Content-Type", "application/download"
      'Response.addHeader "Accept-Header", Http.
      Response.addHeader "Accept-Ranges", "bytes"
      Response.AddHeader "Content-Type", "application/pdf"
      Response.AddHeader "Content-Transfer-Encoding", "binary"
      Response.AddHeader "Content-Disposition", "attachment; filename=yourpdffile.pdf"
      Response.BinaryWrite yourpdffile.content

  33. Pingback: Streaming file to Android Client | BlogoSfera

  34. Pingback: Streaming file to Android Client | BlogoSfera

  35. Hi -

    Firstly, just wanted to say thank you for this helpful article. I really appreciate it.

    Secondly, as my problem was also of the POST/GET variety, I wanted to post my solution. Like others, all was fine with my PDF export code except when viewed in an Android tablet. The original code was (and still is) pretty simple:

    1) With Javascript, build up some HTML into a string
    2) Put this string into a hidden textarea via $(“txtHTML”).val(theHtml). That textarea is inside a hidden form that POSTs to an .ashx handler that generates the PDF.
    3) With jQuery, submit the form

    To make this work for the Android tablet, I had to make the following changes:

    2) Take the HTML string and post it with a jQuery $.ajax(…) call to an .ashx handler that stores the HTML in a session variable
    3) Include a button on the page that goes to another .ashx handler that (a) retrieves the data in the session variable, and then (b) generates the PDF

    Hope this helps someone out there who may find this useful.

    Thanks,
    Chris

  36. Ivan sagt:

    Just a note for those who are still having problems downloading certian types of files Try removing the “Content-Type” line altogether. It might not be the cleanest way to get it working, but it’s the only way I’ve gotten it to work on some file extensions.

  37. alan sagt:

    Hi

    I’ve jsut been fighting Android for a similar issue. I have been writing a document management system for Joomla and the Nexus 4 won’t open PDF’s unless they have a capitalised file type. I’d have never figured that out – it seems mad!

  38. Vijay sagt:

    Hi,

    My name is vijay. I am trying to download mp3 files from android browser with a php script. I followed the instructions in the site, but the files are still downloaded as .htm. Can any one give me some suggestions to solve this problem.

    These are the headers:

    header(“Content-Type: application/octet-stream”);
    header(“Content-Disposition: attachment; filename=”myfile.MP3″”);

    I also changed the content type to “audio/mpeg”. But it did not worked.

  39. AYO sagt:

    I have a problem downloading video files on mobile devices, I have tried all you said yet its not downloading properly. The device will say download successfully but will not play the video file, the size will also be 0kb.
    Here is a download link for you to try and see if there is anything you can do to help,

    http://apps.v2nportal.com/services/content/display.php?pid=2661&user=eportal&authid=2468.
    I have also remove the user and authid parameters before yet it did not work.

  40. Kisaki sagt:

    I have tried all the possibilities of downloading pdf files on android phones but the download will fail using stock, chrome browser all it does is then stops with message Unsuccessful and without a name.

    I tested the codes in Android version 2.3.and 4.2, But using firefox for android, the downloads works without a problem, what can i do for this?

  41. Pingback: Streaming file to Android Client | Technology & Programming

  42. Rajeev Raina sagt:

    Hi

    I am tring to use

    Response.Headers.Add(“Content-Disposition”, String.Format(“attachment; filename=””{0}”””, info.Filename.Replace(“_”, “”).Replace(“-”, “”).ToLower))
    Response.Headers.Add(“Content-Type”, “application/pdf”)

    on IIS6, but it throws me an error “IIS integrated pipeline mode”. I can understand the error as this is a feature of IIS7 onwards.

    Is there an alternate way where I can make it run on IIS6 as I have windows 2003 server.

    My problem is on Android default browser all my file which I download are saved as HTML.

    Looking forward to your reply

    Regards

  43. Nidal Hajaj sagt:

    After much searching I found my solution to the form post download issue downloading a html page was to change the form submit to a GET rather than a POST. There is an issue with a double get request that occurs on the stock android browers explained in the answer from StevePayne here:

    http://stackoverflow.com/questions/4674737/avoiding-content-type-issues-when-downloading-a-file-via-browser-on-android/5728859#comment46671021_5728859

    Change your POST to a GET and everything should be fine, along with the correct headers explained in previous posts.

  44. Frank sagt:

    I had problems with file download on android as well (asp.net mvc 5, server is running IIS). What solved it for me was to submit the page (i just did this through javascript), then the action method returned the file. So just through javascript set the action for the form before you submit it.

Hinterlasse einen Kommentar zu Semika Antworten abbrechen

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind markiert *

Du kannst folgende HTML-Tags benutzen: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>