I'm writing a module based on this Jeff Geerling post that will allow the user to use a form to select some data, and then use the Batch API to generate a large .csv file with the desired data and download it from the browser. I have the form and batch processing working fine, but I can't figure out how to determine the path for the created file, as referenced in this code snippet:

 * Interim download step for downloading CSV file.
function MYMODULE_download_csv_file_interim($list_id) {
  global $base_url;

  if (empty($_SESSION['csv_download_filename']) || empty($_SESSION['csv_download_file'])) {
    return t('Please visit your list subscribers page to begin a list download.');

  $list = node_load($list_id);

  // Redirect to the download file.
  $redirect = base_path() . 'path/to/download/csv/' . $list_id;
  drupal_add_js('setTimeout(function() { window.location.href = "' . $redirect . '"; }, 2000);', 'inline');

  $download_link = l(t('click here to download the file'), 'path/to/download/csv/' . $list_id);
  $output = '<p>' . t('Your subscriber list is now ready for download. The download should begin automatically. If it does not begin downloading within 5 seconds, please !download_link.', array('!download_link' => $download_link)) . '</p>';
  $output .= '<p>' . l(t("&#8592; Back to %list subscribers", array('%list' => $list->title)), 'node/' . $list_id . '/subscribers', array('html' => TRUE)) . '</p>';
  return $output;

At this point in the code, $_SESSION['csv_download_file'] is tmp/my_file.csv. I've tried to figure how to make this a valid url for downloading the file, but can't. For instance, I've tried using file_create_url(), but it just returns the same path.

What do I need to use to make this a valid URL to pass to the browser?


/tmp is outside of webroot so apache (or whatever) can't serve files from there.

You have 2 options:

  1. Move the file from /tmp to your public files directory. This will mean anyone will have access to it (if they know the url). See file_unmanaged_move().


$source = '/tmp/my_file.csv';
$destination = 'public://csv/my_file.csv';
file_unmanaged_move($source, $destination);
  1. Stream the file to the browser via php. This means only people who can access your php script can get access to the file.


header('Content-type: text/csv');
header('Content-disposition: attachment;filename=my_file.csv');
return readfile('/tmp/my_file.csv');
| improve this answer | |

The solution to my problem actually came in modifying this line:

$redirect = base_path() . 'path/to/download/csv/' . $list_id;

to point to another page instead of a file. I actually was already doing what Felix suggested above, and am using readfile() in this function:

function mymodule_download_csv_file() {
  // For added security, make sure the beginning of the path is the same as that
  // returned by file_directory_temp() (to prevent users from gaining access to
  // arbitrary files on the server).
  if (strpos($_SESSION['csv_download_file'], file_directory_temp()) !== 0) {
    return 'Access denied.';

  // Add HTTP headers for CSV file download.
  drupal_add_http_header('Content-Type', 'text/csv; utf-8');
  drupal_add_http_header('Content-Disposition', 'attachment; filename=' . $_SESSION['csv_download_filename'], TRUE);

  // Allow caching, otherwise IE users can't dl over SSL (see issue #294).
  drupal_add_http_header('Cache-Control', 'max-age=300; must-revalidate');

  // Read the file to the output buffer and exit.

So the trick was simply to add a menu item for this function, and then change the line above to point to the defined URL for this function.

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.