Fail downloading file on Android

How hard is it, to write a PHP script to allow user to download a file? really easy, one would say. Just send the appropriate HTTP header and echo the file content.

1
2
3
4
5
< ?php
  header('Content-type: application/force-download');
  header('Content-Disposition: attachment; filename="test.txt"');
  echo(file_get_content('/path/to/file'));
?>

Basically that’s all I need to force download per PHP. But somehow Android stock browser (Chrome) only downloads and creates a 0 kB file. Other browser (tested with Firefox on Android) saves the file correctly.

What does happen here?

Main task is to write a TYPO3 extension, where user gets a list of files, user can select files and put them in to download basket and with download the files by clicking a download button. Really easy task, doesn’t it?

  1. Generate list of files ✔
  2. put file’s identifier in user’s session ✔
  3. if download button is clicked, get file identifier from session and update the session ✔
  4. send HTTP header and the file ✔

simple pseudocode:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php
 
function main() {
  if (selected_file) {
    // add file identifier to session
  }
 
  if (download_click){
    // get file_identifier from session
    // remove the file_identifier from session
    // send HTTP header
    // send the file
    exit;
  }
 
  // create the file list
  return create_filelist();
}
 
?>

Everything are implemented and successfully tested, on desktop browser at least. On mobile, specially on Android (mobile or tablet) something weird is happening. Every time the file is downloaded, the file browser shows a 0 kB file was saved. The file has the correct name. So it must be something with the HTTP header.

After putting the tablet into debugging mode and connect to Chrome on desktop, I can’t see which the HTTP header is arrived to the device. So let’s see the Apache log on the server. To my astonishment Apache receives 2 GET request from the devices

"GET /index.php HTTP/1.1" 200 2556 "-" "Mozilla/5.0 (Linux; Android 5.1.1; Nexus 7 Build/LMY48M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.94 Safari/537.36"
"GET /index.php HTTP/1.1" 200 - "" "Mozilla/5.0 (Linux; Android 5.1.1; Nexus 7 Build/LMY48M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.94 Safari/537.36"

The server responds for the second request with an empty result. But why does Android sends the same request twice? This behaviour is reported on Android AOSP bug tracker here and here. The reason for double request is simple but somehow annoying. According to this android (or the browser) needs to hit the server first. If the HTTP header signalises, that a file will be sent, the browser starts download manager and hits the server again for receiving the file.

So this is client problem, so we need to change our code implementation. Instead removing the file identifier from session before sending the file, we need to check if the connection is aborted before removing file identifier from the session. So that on the second request, the file is still in the session and sent one more time. Let’schange the code starting from line 8.

8
9
10
11
12
13
14
15
16
17
18
  if (download_click){
    // get file_identifier from session
 
    // send HTTP header
    // send the file
 
    if (!connection_aborted()) {
      // remove the file_identifier from session
    }
    exit;
  }

This double-request-problem only occurs on Android stock browser (Chrome). On Firefox the file is downloaded normally and only one request is sent to the server.

Leave a Reply

Your email address will not be published. Required fields are marked *