I am writing my output to the text file and then downloading it using php but issue is that it is saving the output but it is also saving the whole structure of HTML into the textfile also. I don't know why its happening tried to solve it but did'nt figure out how.
I want to save output from fwrite($fh, $formatted_url."\n");
Below is my code:
function get_m3u8_video_segment($url,$portnum=80,$from,$to)
{
$file_name="urlscan.txt";
$fh = fopen($file_name, 'w') or die("Unable to open file!");
for ($x = $from; $x <= $to; $x++)
{
$formatted_url="{$url}:{$portnum}/s-{$x}.m3u8";
//echo "URL is: $formatted_url <br>";
//$contents = file_get_contents($formatted_url);
$contents = get_web_page( $formatted_url );
if ((strpos($contents, 'not found') !== false)||(strpos($contents, 'EXTM3U') !== false))
{
echo" $formatted_url<br>";
fwrite($fh, $formatted_url."\n");
}
}
//header download
header("Content-Disposition: attachment; filename=\"" . $file_name . "\"");
header("Content-Type: application/force-download");
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header("Content-Type: text/plain");
}
get_m3u8_video_segment($url,$portnum,$from,$to);
}
If there is other HTML content elsewhere in your PHP script then this will also be outputted as it normally is, except in this case it will become part of the downloaded file. If you don't want that to happen then you have to stop your script with an exit(); command after you have output the content you actually want. In your script, it looks like you can probably do this just after the call to the function. (But if you have already output some HTML before this, you'll need to alter your script more substantially.)
N.B. I'm surprised you aren't getting a warning about headers being already sent? That normally happens if you try to set headers after you've already echoed some content. Check your log files. Normally you are supposed to output the headers first.
Also, unless you are wanting to keep it for some other purpose, there is no use in saving anything to urlscan.txt - it is not playing any part in your download process. And it would get overwritten every time this script is executed anyway. The headers will cause the browser to treat the output contents (i.e. anything which the PHP script sends to the regular output) as a text file - but this is not the same file as the text file on your server's disk, and its contents can be different.
You happen to be outputting similar content (via echo" $formatted_url<br>";) as you are adding to the urlscan file (via fwrite($fh, $formatted_url."\n");) and I think this may be confusing you into thinking that you're outputting the contents of urlscan.txt, but you aren't - your PHP headers are telling the browser to treat the output of your script (which would normally just go onto the browser window as a HTML page) as a file - but it's a) a new file, and b) actually isn't a file at all until it reaches the browser, it's just a response to a HTTP request. The browser turns it into a file on the client machine because of how it interprets the headers.
Another thing: the content you output needs to be in text format, not HTML, so you need to change the <br> in your echo to a \n.
Lastly, you're outputting the content-type header twice, which is nonsense. A HTTP request or response can only have one content type. In this case, text/plain is the valid MIME type, the other one is not real.
Taking into account all of the above, your code would probably be better written as:
function get_m3u8_video_segment($url, $portnum=80, $from, $to)
{
//header download
$file_name="urlscan.txt";
header("Content-Disposition: attachment; filename=\"" . $file_name . "\"");
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header("Content-Type: text/plain");
for ($x = $from; $x <= $to; $x++)
{
$formatted_url="{$url}:{$portnum}/s-{$x}.m3u8";
$contents = get_web_page( $formatted_url );
if ((strpos($contents, 'not found') !== false)||(strpos($contents, 'EXTM3U') !== false))
{
echo" $formatted_url\n";
}
}
}
get_m3u8_video_segment($url, $portnum, $from, $to);
exit();
Related
I got stuck with the code left by the first programmer of the current webpage that I'm on. He create his own custom framework.
My problem is that I have created a function for file export cause there is a feature wherein I should export the query to textfile so client/user download it automatically via forcing the browser to download the generated file: heres the code of my force download.
Note: I have already search with others forums as well as at Stackoverflow but I can't find an answer. This is my current code:
if(!defined('APPLICATION_RUNNING')){
header("HTTP/1.0 404 Not Found");
die('acces denied');
}
if(defined('ANNOUNCE')){
echo "\n<!-- loaded: ".__FILE__." -->\n";
}
if(!class_exists('APP_Ajax')){
class APP_Ajax extends APP_Base_Ajax
{
function textfiles()
{
global $apptemplate, $appform, $appdb;
if(!$appform->form_valid($this->post['formid'])) // if not b\valid form
{
json_return_error(5);
}
else
{
if(!empty($this->post['form'])) //if the post of form is not empty
{
$e = array();
}
else
{
json_return_error(6);//alert if form is empty
}
if(empty($this->post['form']['rowid']))
{
json_return_error(252); // if rowid is empty then raise error
}
else
{
$rets = $this->post['form']['rowid'];
$ids = explode(',',$rets);
foreach ($ids as $kids)
{
$sql = pg_query("SELECT * from tbl_patient WHERE id='$kids'");
$info = pg_fetch_assoc($sql);
$text[] = ucwords($info['firstname']." ".$info['mi']." ".$info['lastname']).",".$info['medicalrecordnumber'].",".$info['referraldate'];
}
$output = implode( "\r\n" , $text );
$randfile = rand(12345689,999999999)."_".date('m-d-Y');
$f = fopen("templates/default/exports/".$randfile.".txt", "w");
fwrite($f, $output);
fclose($f);
$file_name = ABS_PATH.'templates/default/exports/'.$randfile.'.txt';
header('Pragma: public'); // required
header('Expires: 0');// no cache
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Last-Modified: '.gmdate ('D, d M Y H:i:s', filemtime ($file_name)).' GMT');
header('Cache-Control: private',false);
header('Content-Type: text/plain');
header('Content-Disposition: attachment; filename="'.basename($file_name).'"');
header('Content-Transfer-Encoding: binary');
header('Content-Length: '.filesize($file_name));
header('Connection: close');
readfile($file_name);// push it out
}
}
}
}
The code above produce no error, the file where created on the path I create but
the problem is that "No save as" or something, the browser do not download a file. When I use httpfox to view the headers, etc, etc, the result of the query is displayed on httpfox content tab, so nothings wrong with the code except that I want the browser to show the Download file(Save as dialog) so the user can save it to there Hard drives..
Note 2#: When I create a download link like Download, when I click the download link, the page returns me to the main page and no downloads at all.
Please give me a script or advice on how to work on this stuff. Thank you in advance.
Tell me if I'm missing something here.
Oops forgot to tell you, the website I'm on it do not allow external access to file for security that's why I can't link the export file it to other page for download. :)
I am using php script to provide download from my website after a requisite javascript timer this php script is included which causes the download. But the downloaded file is corrupt no matter whatever I try. Can anyone help me point out where am I going wrong.
This is my code
<?php
include "db.php";
$id = htmlspecialchars($_GET['id']);
$error = false;
$conn = mysql_connect(DB_HOST,DB_USER,DB_PASSWORD);
if(!($conn)) echo "Failed To Connect To The Database!";
else{
if(mysql_select_db(DB_NAME,$conn)){
$qry = "SELECT Link FROM downloads WHERE ID=$id";
try{
$result = mysql_query($qry);
if(mysql_num_rows($result)==1){
while($rows = mysql_fetch_array($result)){
$f=$rows['Link'];
}
//pathinfo returns an array of information
$path = pathinfo($f);
//basename say the filename+extension
$n = $path['basename'];
//NOW comes the action, this statement would say that WHATEVER output given by the script is given in form of an octet-stream, or else to make it easy an application or downloadable
header('Content-type: application/octet-stream');
header('Content-Length: ' . filesize($f));
//This would be the one to rename the file
header('Content-Disposition: attachment; filename='.$n.'');
//Finally it reads the file and prepare the output
readfile($f);
exit();
}else $error = true;
}catch(Exception $e){
$error = true;
}
if($error)
{
header("Status: 404 Not Found");
}
}
}
?>
This helped me in case of more output buffers was opened.
//NOW comes the action, this statement would say that WHATEVER output given by the script is given in form of an octet-stream, or else to make it easy an application or downloadable
header('Content-type: application/octet-stream');
header('Content-Length: ' . filesize($f));
//This would be the one to rename the file
header('Content-Disposition: attachment; filename='.$n.'');
//clean all levels of output buffering
while (ob_get_level()) {
ob_end_clean();
}
readfile($f);
exit();
First of all, as some people pointed out on the comments, remove all spaces before the opening PHP tag (<?php) on the first line and that should do the trick (unless this file is included or required by some other file).
When you print anything on the screen, even a single space, your server will send the headers along with the content to be printed (in the case, your blank spaces). To prevent this from happening, you can:
a) not print anything before you're done writing the headers;
b) run an ob_start() as the first thing in your script, write stuff, edit your headers and then ob_flush() and ob_clean() whenever you want your content to be sent to the user's browser.
In b), even if you successfully write your headers without getting an error, the spaces will corrupt your binary file. You should only be writing your binary content, not a few spaces with the binary content.
The ob_ prefix stands for Output Buffer. When calling ob_start(), you tell your application that everything you output (echo, printf, etc) should be held in memory until you explicitly tell it to 'go' (ob_flush()) to the client. That way, you hold the output along with the headers, and when you are done writing them, they will be sent just fine along with the content.
ob_start();//add this to the beginning of your code
if (file_exists($filepath) && is_readable($filepath) ) {
header('Content-Description: File Transfer');
header("Content-Type: application/octet-stream");
header("Content-Disposition: attachment; filename=$files");
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Pragma: public');
header("content-length=".filesize($filepath));
header("Content-Transfer-Encoding: binary");
/*add while (ob_get_level()) {
ob_end_clean();
} before readfile()*/
while (ob_get_level()) {
ob_end_clean();
}
flush();
readfile($filepath);
In my function I am saving an image decoded from a base64 string:
function saveImage(){
//magic...
define('UPLOAD_DIR', '../webroot/img/');
$base64string = str_replace('data:image/png;base64,', '', $base64string);
$base64string = str_replace(' ', '+', $base64string);
$data = base64_decode($base64string);
$id = uniqid();
$file = UPLOAD_DIR.$id.'.png';
$success = file_put_contents($file, $data);
}
The above function works properly and the images are saved and not corrupted in the specified folder.
In the next function I am now trying to force download the image to a user:
function getChart($uniqid= null){
if($uniqid){
$this->layout = null;
header("Content-type: image/png");
header("Content-Disposition:attachment;filename='".$uniqid.".png'");
readfile('../webroot/img/'.$uniqid.'.png');
exit;
} else exit;
}
Image downloaded from the server is corrupted and cant be displayed. After opening the downloaded file in a text editor I noticed that a new line character is added at the very top. After deleting the character and saving the file it opens properly and is being displayed properly.
How can I fix this?
What you describe can have multiple issues that are hidden until you actually open the downloaded file.
Instead make your code more robust and check pre-conditions, here if headers have been send already and to clean any possible existing output buffer and give error if that is not possible:
function getChart ($uniqid = null) {
if (!$uniqid) exit;
$this->layout = null;
if (headers_sent()) throw new Exception('Headers sent.');
while (ob_get_level() && ob_end_clean());
if (ob_get_level()) throw new Exception('Buffering is still active.');
header("Content-type: image/png");
header("Content-Disposition:attachment;filename='".$uniqid.".png'");
readfile('../webroot/img/'.$uniqid.'.png');
exit;
}
header("Content-length: $file_size")
This header tells the browser how large the file is. Some browser need it to be able to download the file properly. Anyway it's a good manner telling how big the file is. That way anyone who download the file can predict how long the download will take.
header("Content-type: $file_type")
This header tells the browser what kind of file it tries to download.
header("Content-Disposition: attachment; filename=$file_name");
This tells the browser to save this downloaded file under the specified name. If you don't send this header the browser will try to save the file using the script's name.
BUT you need to flush buffer, with flush(); or in your case with ob_flush(); right above first exit;
check if you are outputing something before calling readfile:
// add ob_start() at the very top of your script
function getChart($uniqid= null){
echo strlen(ob_get_clean()); die(); // if it's not 0 then you are definetly echoing something
if($uniqid){
$this->layout = null;
header("Content-type: image/png");
header("Content-Disposition:attachment;filename='".$uniqid.".png'");
readfile('../webroot/img/'.$uniqid.'.png');
exit;
} else exit;
}
EDIT (to force download):
function getChart($uniqid= null){
if($uniqid){
$image = $uniqid;
header("Cache-Control: public");
header("Content-Description: File Transfer");
header("Content-Disposition: attachment; filename=" . $image);
header("Content-Type: image/jpg");
header("Content-Transfer-Encoding: binary");
readfile($image);
} else exit;
}
getChart("060620121945.jpg");
Try this (just to render image):
function getChart($uniqid= null){
if($uniqid){
$mime_type = "image/png";
$content = file_get_contents('../webroot/img/'.$uniqid.'.png');
$base64 = base64_encode($content);
return ('data:' . $mime_type . ';base64,' . $base64);
} else exit;
}
I have had this problem several times. Here's the solution that I used:
Almost always it turned out that I had forgotten to turn off unicode BOM encoding in my download.php file which adds something to the front of the downloaded file and therefore corrupts it.
So the solution is to turn off unicode BOM in download.php.
Just use ob_clean(); before readfile()
I try export data from database to .csv. When I click export link I don't see save window in browser for a very long time if there is quite a lot amount of data. It can be quite confusing if the script looks like hanging for some time and after quite a long time one can see save window in browser.
The code is something like this in controller:
$this->_helper->layout->disableLayout();
$this->_helper->viewRenderer->setNoRender();
$fileName = $list->list_name . '.csv';
$this->getResponse()->setHeader('Content-Type', 'text/csv; charset=utf-8')
->setHeader('Content-Disposition', 'attachment; filename="'. $fileName . '"');
$contacts = new Contact();
$contacts->export($listId);
Export method reads records one by one and prints it something like this:
$fp = fopen('php://output', 'w');
foreach ($mongodbCursor as $subscriber) {
$row = formRow($subscriber);
fputcsv($fp, $row);
}
I see on some applications that save winow appear almost immediately and when you click save you see progress of downloading.
I tried to replace:
$this->getResponse()->setHeader('Content-Type', 'text/csv; charset=utf-8')
->setHeader('Content-Disposition', 'attachment; filename="'. $fileName . '"');
with this one:
header('Content-Type: text/csv; charset=utf-8');
header('Content-Disposition: attachment; filename="'. $fileName . '"');
It didn't help so far.
I wonder if it's possible to send headers before all data are read one by one from database?
Thank your for your assistance.
Hmm I'm not familiar with php://output, my application writes my information with fopen,fwrite,fclose to a temporary file afterwards I give it out with similiar header(); options.
$filesize = filesize("tmp/export.csv");
header("Content-Type: text/csv");
header("Content-Disposition: attachment; filename=\"export.csv\"");
header("Conent-Length: $filesize");
readfile("tmp/export.csv");
unlink("tmp/export.csv");
exit;
This one gives the download window of your browser instantly.
You could try to do this:
call the header function instead of $this->getResponse()->setHeader() (the response content might be saved in a buffer and outputed only when it is completed - the time the export finishes)
try to echo the content directly instead of writing to php://output (if you set the headers before that, everything you echo will be placed in the generated CSV file)
EDIT
Replace fputcsv with a function like print_row below
function print_row($row) {
echo '"' . implode('","', $row) . '"' . "\r\n";
}
The function gets the first parameter as an array, adds " and , and echoes to content.
// $file = path of file
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename='.basename($file));
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' . filesize($file));
ob_clean();
flush();
readfile($file);
exit;
Source: http://php.net/manual/en/function.readfile.php
Try using application/force-download instead of text/csv in your Content-Type value.
header("Content-Type: application/force-download; name=\"" . $fileName . "\"");
This will force an actual download box instead of just showing the content in the browser.
Here is some documentation from MIME Application:
Mime application/force-download, which is typically expressed as mime application/x-force-download, is typically used in conjunction with PHP code to trigger a download box rather than for displaying content in a Web browser.
For example, if you had a file on your website that you want the user to download rather than view in the Web browser, you could enter the appropriate mime application/force-download code in the file’s header content type field. Note that mime application/force-download is not a registered MIME type. In addition, when used in PHP code, the preferred spelling of mime application/force-download contains the “x-” prefix.
I don't know if this a good solution, I didn't explore it much, but it seems it's working.
It cotroller after I add some data to buffer after headers, before exporting.
header('Content-Type: text/csv; charset=utf-8');
header('Content-Disposition: attachment; filename="'. $fileName . '"');
echo str_pad('', ini_get('output_buffering'));
ob_flush();
flush();
Model_Subscriber1::exportList($listId);
To make it works in controller I added in Zend Bootstrap.php:
/**
* hint to the dispatcher that it should not use output buffering to capture output generated by action controllers.
* By default, the dispatcher captures any output and appends it to the response object body content.
*/
protected function _initFront()
{
$frontController = Zend_Controller_Front::getInstance();
$frontController->setParam('disableOutputBuffering', 1);
}
It look like in this case I get download window in browser quickly and then data are exporting which could take quite a long time. I don't know if this solution is acceptable now. I'd be glad to here your opinion about it.
It cause issues with adding empty data to export file.
So, I change it controller to.
header('Content-Type: text/csv; charset=utf-8');
header('Content-Disposition: attachment; filename="'. $fileName . '"');
Model_Subscriber1::exportList($listId);
And chang function for export to something like this:
foreach ($mongodbCursor as $subscriber) {
$row = formRow($subscriber);
echo '"' . implode('","', $row) . '"' . "\r\n";
ob_flush();
flush();
}
Your function may want to call flush / ob_flush just before long operation and let the HTTP header send to client before long process.
I am trying to read a image file (.jpeg to be exact), and 'echo' it back to the page output, but have is display an image...
my index.php has an image link like this:
<img src='test.php?image=1234.jpeg' />
and my php script does basically this:
1) read 1234.jpeg
2) echo file contents...
3) I have a feeling I need to return the output back with a mime-type, but this is where I get lost
Once I figure this out, I will be removing the file name input all together and replace it with an image id.
If I am unclear, or you need more information, please reply.
The PHP Manual has this example:
<?php
// open the file in a binary mode
$name = './img/ok.png';
$fp = fopen($name, 'rb');
// send the right headers
header("Content-Type: image/png");
header("Content-Length: " . filesize($name));
// dump the picture and stop the script
fpassthru($fp);
exit;
?>
The important points is that you must send a Content-Type header. Also, you must be careful not include any extra white space (like newlines) in your file before or after the <?php ... ?> tags.
As suggested in the comments, you can avoid the danger of extra white space at the end of your script by omitting the ?> tag:
<?php
$name = './img/ok.png';
$fp = fopen($name, 'rb');
header("Content-Type: image/png");
header("Content-Length: " . filesize($name));
fpassthru($fp);
You still need to carefully avoid white space at the top of the script. One particularly tricky form of white space is a UTF-8 BOM. To avoid that, make sure to save your script as "ANSI" (Notepad) or "ASCII" or "UTF-8 without signature" (Emacs) or similar.
I feel like we can make this code a little bit easier by just getting the mime type from $image_info:
$file_out = "myDirectory/myImage.gif"; // The image to return
if (file_exists($file_out)) {
$image_info = getimagesize($file_out);
//Set the content-type header as appropriate
header('Content-Type: ' . $image_info['mime']);
//Set the content-length header
header('Content-Length: ' . filesize($file_out));
//Write the image bytes to the client
readfile($file_out);
}
else { // Image file not found
header($_SERVER["SERVER_PROTOCOL"] . " 404 Not Found");
}
With this solution any type of image can be processed but it is just another option. Thanks ban-geoengineering for your contribution.
I worked without Content-Length . maybe reason work for remote image files
// open the file in a binary mode
$name = 'https://www.example.com/image_file.jpg';
$fp = fopen($name, 'rb');
// send the right headers
header('Cache-Control: no-cache, no-store, max-age=0, must-revalidate');
header('Expires: January 01, 2013'); // Date in the past
header('Pragma: no-cache');
header("Content-Type: image/jpg");
/* header("Content-Length: " . filesize($name)); */
// dump the picture and stop the script
fpassthru($fp);
exit;
Very, very easy.
<?php
//could be image/jpeg or image/gif or whatever
header('Content-Type: image/png')
readfile('image.png')
?>
This should work. It may be slower.
$img = imagecreatefromjpeg($filename);
header("Content-Type: image/jpg");
imagejpeg($img);
imagedestroy($img);
Another easy Option (not any better, just different) if you aren't reading from a database is to just use a function to output all the code for you...
Note: If you also wanted php to read the image dimensions and give that to the client for faster rendering, you could easily do that too with this method.
<?php
Function insertImage( $fileName ) {
echo '<img src="path/to/your/images/',$fileName,'">';
}
?>
<html>
<body>
This is my awesome website.<br>
<?php insertImage( '1234.jpg' ); ?><br>
Like my nice picture above?
</body>
</html>