PHP writing CSV, outputting the entire webpage for some reason - php

function outputCSV($data) {
$outstream = fopen("php://output", 'w');
function __outputCSV(&$vals, $key, $filehandler){
fputcsv($filehandler, $vals, ',', '"');
}
array_walk($data, '__outputCSV', $outstream);
fclose($outstream);
}
function someFunctionInTheBigPHPFile() {
header("Content-type: text/csv");
header("Content-Disposition: attachment; filename=file.csv");
header("Pragma: no-cache");
header("Expires: 0");
$mydata = array(
array('data11', 'data12', 'data13'),
array('data21', 'data22', 'data23'),
array('data31', 'data32', 'data23'));
outputCSV($mydata);
exit;
}
The output CSV does contain the data array. The problem is, this array is displayed along with the rest of the webpage, that is everything before this function is called and everything that comes after it, despite these two functions being the only ones that deal with any fopen and writing to files.
How can I stop the rest of the webpage from interfering? I only want the data array in the CSV..
EDIT: I managed to chop off everything succeeding my array by adding exit;, but I still have the problem of the entire website being displayed before the array.

Stop execution after outputting the CSV data. You can do this with die() or exit().

At the beginning of the PHP file, check straight away if you want to print your CSV (this is probably passed through $_POST or $_GET). If so, run it straight through the function and end that function with an exit;.
This prevents anything from happening before or after the CSV is created. For some reason all code on the page is included in the new file, even if the file stream was opened and closed at times independant of when the page's content was computed.
And this effectively leaves you with only what you wanted, not the rubbish before or after it.

Maybe i misunderstood you, but someFunctionInTheBigPHPFile() prints out the file to the screen. So, why are you using this i you dont want to a screen output ?

Related

php write to file without prompting for download

The following code works fine for writing to a file, but I don't want it prompts the user to download whenever it executes. I would like to prevent this... Any help would be appreciatied. I've also tried it with fwrite with the same results.
$file_name = 'orders'.date('Ymd').'csv';
header('Content-Type: application/excel');
header('Content-Disposition: attachment; filename="'.$file_name.'"');
$data = array(
'aaa,bbb,ccc,dddd',
'123,456,789',
'"aaa","bbb"'
);
$fp = fopen('php://output', 'w');
foreach ( $data as $line )
{
$val = explode(",", $line);
fputcsv($fp, $val);
}
fclose($fp);
There is not possibility to do that (or even if there is some trick, you never should use it!).
Only way to avoid prompt is to change browser settings which of course can be done by user, not by you.
It'll not be safe for user if anybody would be able to save files on his computer without any prompt.
Remove the lines
header('Content-Type: application/excel');
header('Content-Disposition: attachment; filename="'.$file_name.'"');
and it will just write the file.
As it is, it does not appear to be outputting any content anyhow.
You are mixing up how HTTP (headers in particular) works and local files.
The way a HTTP response is interpreted by the browser can be suggested by using headers (content-type, content-disposition).
The way a file is interpreted by your operating system and applications has nothing to do with HTTP and HTTP headers.
Your script basically does two things:
tells the browser (through HTTP headers) to interpret the response as an application/excel attachment (triggers download and passes responsibility to the operating system);
writes some stuff to a file.
You need to skip step (1).
Also, if you don't care about performance in special cases, file_put_contents should be enoguh.

What does the "PK¿¿¿" response means in PHP

Hi I'm downloading a file to an app on iOS using the function readfile() on a PHP web service and I want to know if the file is downloaded correctly but I don't know how I can do that.
So what I'm trying is to do some echo to know if the file has been downloaded like this:
echo "before";
readfile($file);
echo "after";
But the response I get is this:
beforePK¿¿¿
Any one knows what does this mean or how can I know if the file is downloaded correctly?
UPDATE:
Yes it's a zip file, here are my headers
header("Cache-Control: public");
header("Content-Description: File Transfer");
header("Content-Disposition: attachment; filename=$ticket");
header("Content-Type: application/zip");
header("Content-Transfer-Encoding: binary");
You're trying to output the contents of a zip file aren't you?
readfile($file) works the same as echo file_get_contents($file). If you're trying to present someone a file to download, do not add any additional output else you risk breaking the file.
I would also recommend reading up on the header function. That way you can explicitly tell the browser that you're sending a file, not an HTML page that has file-like contents. (See the examples involving Content-Type)
PHP should be setting the correct headers prior to readfile() - this LITERALLY reads the file out to the browser/app... but the browser/app needs to know what to do with it...
Usually you just assume that once the connection has closed that the data is done being transferred. If you want to validate that the file has been transferred fully, and without corruption you'll need to use a data structure like XML or JSON which will:
Delimit the data fields and cause the XML/JSON parser to throw an error if one is omitted, aka the transfer was cut off before it finished.
Allow you to embed more than one piece of data with the response, eg. an MD5 hash of the file that can be re-calculated client-side to verify that the data is intact.
eg:
$file = 'myfile.zip';
$my_data = array(
'file' => base64_encode(file_get_contents($file)),
'hash' => md5_file($file)
)
//header calls
header(...)
echo json_encode($my_data);
exit;

Save multiple arrays to CSV from PHP

There are two arrays that I'd like to save as CSV file from PHP. The problem is that this code works only for the first Call. E.g. in the below-given example I can save only array1, but array2 is not saved. If I swap places of array1 and array2, then array2 will be saved instead of array1. What's actually wrong in my code and how could I solve this problem?
header("Content-type: text/csv");
header("Pragma: no-cache");
$headers = array('xxx','yyy','zzz');
saveCSV($array1,"file1.csv",$headers);
header("Content-type: text/csv");
header("Pragma: no-cache");
$headers = array('aaa','bbb','ccc');
saveCSV($array2,"file2.csv",$headers);
function saveCSV($data,$fileName,$headers) {
$outstream = fopen($fileName, "a");
file_put_contents($fileName, "");
if ($headers != 0)
fputcsv($outstream,$headers);
function __outputCSV(&$vals, $key, $filehandler) {
fputcsv($filehandler, $vals);
}
array_walk($data, "__outputCSV", $outstream);
fclose($outstream);
}
Your setting the headers, twice.
Trying building up your csv data output, then once completed set the headers once.
Also you have a function declaration within another, which i'm sure is invalid.
move __outputCSV somwehere else, or maybe you intended it to be a closure?
As far as I know, you can only set headers once then return content. The code above sets some headers, replies with CSV, then sets headers again, and replies with a different CSV again. PHP/browser won't handle separate return.
Instead of pushing multiple miles from the server, perhaps you can just have the browser window.open() /path-to-get-csv-script.php?op=file1 and then op=file2. The browser can then open up both files if that's what you want....

PHP fopen contains unwanted content

Of course after hours of pondering this problem, the first comment on my question lead me to solve it immediately.
The problem was that, although I was including this code within its own function at the top of the page, I was calling it only if a certain flag was set in the $_POST array. I wasn't checking for the flag until the end of the PHP file. I moved that check before the function, and it worked.
The original question is below:
I'm trying to use the fopen() function in PHP to output a CSV file, and although it contains the data I want, it also contains the entire HTML structure of the page, as well as inline stylesheets, before the content that I actually want to output.
I'm using this code (from here) pretty much unchanged. I'm very unfamiliar with PHP streaming and output, so I started from what I hope was a firm foundation:
$fileName = 'somefile.csv';
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header('Content-Description: File Transfer');
header("Content-type: text/csv");
header("Content-Disposition: attachment; filename={$fileName}");
header("Expires: 0");
header("Pragma: public");
$fh = #fopen( 'php://output', 'w' );
global $wpdb;
$query = "SELECT * FROM `{$wpdb->prefix}my_table`";
$results = $wpdb->get_results( $query, ARRAY_A );
$headerDisplayed = false;
foreach ( $results as $data ) {
// Add a header row if it hasn't been added yet
if ( !$headerDisplayed ) {
// Use the keys from $data as the titles
fputcsv($fh, array_keys($data));
$headerDisplayed = true;
}
// Put the data into the stream
fputcsv($fh, $data);
}
// Close the file
fclose($fh);
// Make sure nothing else is sent, our file is done
exit;
My assumption is that this example was intended to be included in its own external PHP file, but due to the constraints I'm dealing with, I'm trying to include it inline instead. I've mucked about with output buffering a bit with no positive results, but the PHP documentation on these is quite sparse, so there's probably something I'm missing.
Problem seems to be that, at the same time you try to output from the same PHP file, the CSV file AND some html content. You've got to separate them, to have 2 different URLs.
I guess your PHP code is surrounded by the html code (and css inline) you're talking about.
What you've got to do is:
have a PHP script that only outputs the CSV content (which only contains the code you showed us, with the opening php tag of course)
have another PHP script which produces html code, and provides a link to the previous script (for example).
You are on the right track with the 'include it inline' reason to why you're getting everything else before the data.
This script will need to be it's own separate file called directly, instead of including it inline in another script. I understand you have other database connections and such that have to be set up first. You'll have to extract those out of your standard pages and include those on this page.
Of course after hours of pondering this problem, the first comment on my question lead me to solve it immediately.
The problem was that, although I was including this code within its own function at the top of the page, I was calling it only if a certain flag was set in the $_POST array. I wasn't checking for the flag until the end of the PHP file. I moved that check before the function, and it worked.

How to stop PHP prefixing LF to start of file

I am using PHP to pass some information in a text file back to the user which is then used as input for an app. I'm using the method shown in the following snippet to serve the file to the user.
header('Content-type: text/csv');
header('Content-Disposition: attachment; filename=filename.dat');
echo $data;
exit();
I get the save as dialogue fine and the data is present the only problem is that there is a line feed character at the start of the output file which I cant seem to get rid of.
It just so happens that the app that uses the data is sensitive to white space and although it would be easy to fix this, forcing the users to update is not a route I want to go down for various reasons.
Does anyone know what is causing this and more importantly any workarounds.
As I already said in the comments to the question:
Either you $data contains that line feed or there is something before that snippet that does this. Maybe a line feed before you opened the PHP block.
Probably $data contains the line feed.
Look for includes too
Presumably the extra newline is getting into $data somehow. If you can’t fix that, you could trim() the data before you echo it.
Can you post how you're setting $data?
Just an additional note.
In case you are working on a project where you cannot identify the files that have the leading or trailing line feeds, new lines, carriage returns just call the code below before your headers:
//Loop through any open buffers and nuke them.
while(#ob_end_clean());
header('Content-type: text/csv');
header('Content-Disposition: attachment; filename=filename.dat');
echo $data;
exit();
This may have some unexpected side-effects on the code that relies on those buffers but is an effective way to completely clear out your output buffer.
Maybe you can use ob_get_contents or ob_get_length to see if anything has been sent to the output before the echo statement. Or use ob_clean before the echo.

Categories