Could you suggest a workaround on how to make this PHP code to make a correct .rar file download?

Hi Experts

From a day to another this PHP code that  run to download a correct  .rar  file

// pg_download_patch.php - VIEW part

<div class="form-group">
    <form action="download_patch_gestao.php" method="post">
         <input class="btn btn-primary btn-lg pull-left" id="btn_add" type="submit" name="download_8397" value="Download Atualizacao Gestao Versao 3.500"/>
    </form>
</div>

// download_patch_gestao.php

 <?php

   $post = filter_input(INPUT_POST, "download_8397");

   if ($post)
   {
       $filename = "../hgs_out/Gestao_PATCH_v3500.rar";
       
       header("Content-Type: application/octet-stream");
       header("Content-Length: " . filesize($filename));
       header("Content-Disposition: attachment; filename=" . basename($filename));
       readfile($filename);
  }
?>

Open in new window


Still runs but the .rar downloaded has some kind of error (defective) and couldn't be opened!

Could you suggest a workaround?

(The internet provider initially infomed no configurations was done, any suggestion on what especifically to ask them additionally to check his information?)

Thanks in advance!
Eduardo FuerteDeveloper and AnalystAsked:
Who is Participating?

[Product update] Infrastructure Analysis Tool is now available with Business Accounts.Learn More

x
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

kenfcampCommented:
Have you tested the file to make sure it's not bad?
0
Eduardo FuerteDeveloper and AnalystAuthor Commented:
Hi

Yes, I tested and  it's ok.
If I use FTP to download to a local folder, instead of the PHP page, it's not corrupted and can be unziped.
0
Olaf DoschkeSoftware DeveloperCommented:
Look into max execution time.

Bye, Olaf.
0
OWASP: Forgery and Phishing

Learn the techniques to avoid forgery and phishing attacks and the types of attacks an application or network may face.

Eduardo FuerteDeveloper and AnalystAuthor Commented:
Hi, Olaf

The download time is very normal.
Just the file after downloaded is corrupted,
0
Olaf DoschkeSoftware DeveloperCommented:
Well, the download would perhaps take longer. How large is the file download? If it's the right size, can you peek in with a hex editor to see how much of it is 0-bytes starting from the end?
Also, max execution time is a server side limitation, I'm asking for that, not for your measurement and judgment about the real download time. The script might end before finished due to max execution time. It then ends in a reasonable short time, as the max execution time usually is choosen that way. It's interrupted when the downloader would take longer and then you end up with an incomplete file or a file in the right size, but being created with all 0-byte blocks up front (to not fail on hdd capacity) and only being really loaded partly.

Bye, Olaf.
0
kenfcampCommented:
try changing:
       readfile($filename);

to:
       readfile($filename);
       exit;
0
Eduardo FuerteDeveloper and AnalystAuthor Commented:
@kenfcamp

Unfortunatelly didn't fixed.

@Olaf
Here is some informations - obtained in internet provider:

max_execution_time
Correct file obtainde by via FTP
correct file
Defective file obtained by via PHP page (bigger than the correct one)
Defective file
The size is very near... it looks some extra info is introduced in the defective by the process...
0
Olaf DoschkeSoftware DeveloperCommented:
ma execution: 240 seconds. Is this the download time you have? Then you would be at that cut off.

With a larger file it rather looks like the browser or whatever client tool you use reserves blocks, even more than the file size. and then downloads and fills in the intialized 0 blocks.

You might simply have a problem with the inexactness of the filesize($filename)), when the magnitude of the file size is in that range. That header should specify exact byte size. It should only be a problem with files >2GB, but who knows.

On the other side, you could replace all the headers with a Location header pointing to the rar file directly.

Bye, Olaf.
0
Eduardo FuerteDeveloper and AnalystAuthor Commented:
Well, like I told in the question, the PHP code, the way it's here with or without inexactness of filesize function, had been worked from a long time with absolutelly no problems.

I'm working now in another solution as a workaround.
0
Julian HansenCommented:
Try removing the closing ?> at the end of your script

Then - put the download script in a different file from the form. Make the form point to this script

<?php
  $post = filter_input(INPUT_POST, "download_8397");
   if ($post)
   {
       $filename = "../hgs_out/Gestao_PATCH_v3500.rar";
       
       header("Content-Type: application/octet-stream");
       header("Content-Length: " . filesize($filename));
       header("Content-Disposition: attachment; filename=" . basename($filename));
       readfile($filename);
  }

Open in new window

0
kenfcampCommented:
here you go, should work as expected

 <?php

   if (isset($_POST[download_8397]))
   {

$filename = "../hgs_out/Gestao_PATCH_v3500.rar";
header("Content-Type: application/octet-stream");
header("Content-Length: " . filesize($filename));
header("Content-Disposition: attachment; filename=" . basename($filename));

ob_clean();
flush();

readfile($filename);
exit;

  }else{

print "<div class=\"form-group\">\n
    <form action=\"download_patch_gestao.php\" method=\"post\">\n
         <input class=\"btn btn-primary btn-lg pull-left\" id=\"btn_add\" type=\"submit\" name=\"download_8397\" value=\"Download Atualizacao Gestao Versao 3.500\"/>\n
    </form>\n
</div>\n";

}

?>

Open in new window


Ken
0
Eduardo FuerteDeveloper and AnalystAuthor Commented:
Hi Julian!

The code are still in different files, no success in the attempt.


Ken !!!
Your code worked out like MAGIC!
 
Workes OUT!!!
I'm going to study it deeply!
0
kenfcampCommented:
Eduardo,

I'm glad it helped.

The reason you were having problems with your original code.

Your form html was being appended to the "rar" file and the headers were causing additional spaces to be added as well

The first portion of the script tells the script that if it sees your form field "download_8397" being posted, to deliver the file for download if not, then display the HTML form

This eliminates the first problem

ob_clean();
flush();

Clears header information so spaces aren't added by the "readfile" function
and exit; obviously closes everything so the file isn't modified further

Ken
0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
Eduardo FuerteDeveloper and AnalystAuthor Commented:
Ken

Outstanding commented solution for something that made me worried the last days.


Thanks A LOT!!!
0
kenfcampCommented:
No problem,

Glad it worked out for you
0
Julian HansenCommented:
@Eduardo,

I am glad you are working, however I would like to make a couple of comments on the accepted solution.

Firstly, it still combines the download logic with the form - this is not a good idea. You should separate the functions into different scripts. The reason for this is that when you combine the functionality you create the potential for the scripts to interfere with each other - as I suspect was the cause of your original problem.

Secondly, your download script will not cause a form to navigate away from the page when posted. The form will remain in the browser - a second reason why it is not necessary to combine the logic.

If you take the script from my post and put that in its own file and point the form script at the download script there is no need for the ob_clean() and the flush() statements. These are predicated on you having sent output prior to the start of the download which is then added to the download. If you structure your scripts as suggested and ensure there are no spaces or content before the opening <?php then you have no need for the aforementioned functions.

Here is a sample (shows both use of Form and Link to do the download)

HTML
<h2>Form Example</h2>
<div class="form-group">
<form action="t2887.php" method="post">
   <input class="btn btn-primary btn-lg" id="btn_add" type="submit" name="download_8397" value="Download Atualizacao Gestao Versao 3.500"/>
</form>
</div>
<h2>Link Example</h2>
<a href="t2887-link.php">Download Atualizacao Gestao Versao 3.500</a>

Open in new window

PHP (Form)
<?php
$post = filter_input(INPUT_POST, "download_8397");
if ($post){
  $path = "t2887.rar";
  $filename = 'Gestao_PATCH_v3500.rar';
  header("Content-Type: application/octet-stream");
  header("Content-Length: " . filesize($filename));
  header("Content-Disposition: attachment; filename=" . basename($filename));
  readfile($path);
}

Open in new window

PHP (Link);
<?php
$path = "t2887.rar";
$filename = 'Gestao_PATCH_v3500.rar';
header("Content-Type: application/octet-stream");
header("Content-Length: " . filesize($filename));
header("Content-Disposition: attachment; filename=" . basename($filename));
readfile($path);

Open in new window

Working sample here
0
Olaf DoschkeSoftware DeveloperCommented:
The simplest solution to not have anything in output buffers still is to simply end the script with a Location header meaning a redirect to the download file. Also, that header has to come first in output, but you'll see a warning if you start out with non-PHP:

<?php 
header("Location:somethingelse.html")
?>

Open in new window


Just having space before the <?php I get ERR_NAME_NOT_RESOLVED DNS error in the browser. I think you should already have been teached about the importance of no previous output before headers, no matter if buffered or not. The solution also depends on the nature of PHP to automatically rather not begin output immediately.

The problem you had nevertheless was a bit more delicate, as output worked and created the file.

Bye, Olaf.
0
Julian HansenCommented:
@Olaf,
The issue with a redirect is that it does not always produce the desired results. For instance - let's say you want to force a download of a PDF - if you redirect to it chances are it will load in the browser and not present you with the file save dialog - which may be your intention.
0
Olaf DoschkeSoftware DeveloperCommented:
Well, rar is rarely associated to a browser plug in. Also I wanted to demonstrate what errors a previous output before a header has. In case you use content headers followed by the core content an additional space might not make a big difference, but I would have expected other problems, errors, but not 16 KB difference.

And more generally speaking: If I'd program a software update downloader, I'd let it work via FTP anyway, or other file transfer protocols, not via a php script returning a file. (That means I assume from the logic and the download file name, this rar is containing some software update/patch).

Bye, Olaf.
0
Eduardo FuerteDeveloper and AnalystAuthor Commented:
@Julian

Thank you for your additional participation.
I tested your solution and runs OK - the download is done.

But in my specific case it doesn't run....

My site

Code as exactly is on my site:

form_example.php
<!doctype html>
<html>
<head>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=2.0, user-scalable=yes" />
<title>Download a RAR archive with form and link</title>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css" rel="stylesheet" />
<link href="css/custom.css" rel="stylesheet" />
<style type="text/css">
</style>
</head>
<body>
<div class="wrapper">
  <header>
    <div class="container">
    <h1 class="col-lg-9">Download a RAR archive with form and link</h1>
    
    </div>
  </header>
  <div class="container">
    <h5>Author: Julian Hansen, December 2017</h5>
  <div class="alert alert-success">
    <p>Demonstrates how to download a RAR archive with link and form</p>
  <p><b>NOTE:</b> the RAR archive contains this file and the two PHP scripts for this solution</p>
  </div>
<h2>Form Example</h2>
  <div class="form-group">
    <form action="t2887.php" method="post">
       <input class="btn btn-primary btn-lg" id="btn_add" type="submit" name="download_8397" value="Download Atualizacao Gestao Versao 3.500"/>
    </form>
  </div>
  <h2>Link Example</h2>
   <a href="t2887-link.php">Download Atualizacao Gestao Versao 3.500</a>
</div>
</div>
<footer>
  <div class="container">
    Copyright Julian Hansen &copy; 2017
  </div>
</footer>
<!-- INCLUDE "t2887.php:Form Script" -->
<!-- INCLUDE "t2887-link.php:Link Script" -->
 
 <?php
// Is it really necessary to include? This way  it causes an error...
//    include("t2887.php");
//    include("t2887-link.php");
?>
 
</body>
</html>
 

Open in new window


t2887.php
<?php
$post = filter_input(INPUT_POST, "download_8397");
if ($post){
  $path = "t2887.rar";
  $filename = 'Gestao_PATCH_v3500.rar';
  header("Content-Type: application/octet-stream");
  header("Content-Length: " . filesize($filename));
  header("Content-Disposition: attachment; filename=" . basename($filename));
  readfile($path);
}

Open in new window


One other feature that I omited is that after download a record in a downloads counter table must be done

 <?php

   // Configure DB - needed to open DB
   require ('../config.php');


   if (isset($_POST["download_ajshjdhajsdy2183iu928349238429348"]))
   {

       $filename = "../ahsjasdhajsdjasdh/GESTAO_FULL_v2002.rar";

       header("Content-Type: application/octet-stream");
       header("Content-Length: " . filesize($filename));
       header("Content-Disposition: attachment; filename=" . basename($filename));

       ob_clean();
       flush();

       readfile($filename);

	
       // Added to Ken's solution 
       //Registering DOWNLOADS in a table for statistics => Runs OK!

       $con = mysqli_connect("$host", "$usermysql", "$passmysql", "$db_name");

       if (mysqli_connect_errno())
       {
           echo "Failed to connect to MySQL: " . mysqli_connect_error();
       }

       mysqli_set_charset($con, "utf8");


       $sistema = 'GESTAO_FULL';
       $dia = date("d/m/Y H:i:s");
       $ip = $_SERVER['REMOTE_ADDR'];

       $sql = "INSERT INTO xxxxxx_download(sistema, dia, ip) VALUES ('%s','%s','%s');";

       $comando = sprintf($sql, $sistema, $dia, $ip);
       $result = mysqli_query($con, $comando);

       mysqli_close($con);

       exit;

   } else
   {

       print "<div class=\"form-group\">\n
    <form action=\"download_patch_gestao.php\" method=\"post\">\n
         <input class=\"btn btn-primary btn-lg pull-left\" id=\"btn_add\" type=\"submit\" name=\"download_8397\" value=\"Download Atualizacao Gestao Versao 3.500\"/>\n
    </form>\n
    </div>\n";

   }

?>

Open in new window


Could you possibly give a look?
0
Julian HansenCommented:
Regarding my samples
<!-- INCLUDE "t2887.php:Form Script" -->
<!-- INCLUDE "t2887-link.php:Link Script" -->

Open in new window

The above lines are meta data my sample script uses to determine what files to include in the source code section at the bottom - they are not part of the sample. Only the bits shown in the HTML tab are relevant

config.db is a possible candidate - you are including another file - that could be an issue
Line 29 is a problem - you will add the echo to your download

I would do the database write before the download and log the failure if the DB fails - but not output anything as this will interfere with the download.

I am still against having the if / else for outputting the form in the same script as your download - makes absolutely no sense to me - just makes your script more vulnerable to issues.

The link you provided is erroring for the reasons above - inclusion of lines that should not be there and reference to a RAR file that does not exist.

If you want to use the sample then put this in your html file
  <h2>Form Example</h2>
  <div class="form-group">
    <form action="t2887.php" method="post">
       <input class="btn btn-primary btn-lg" id="btn_add" type="submit" name="download_8397" value="Download Atualizacao Gestao Versao 3.500"/>
    </form>
  </div>
  <h2>Link Example</h2>
   <a href="t2887-link.php">Download Atualizacao Gestao Versao 3.500</a>

Open in new window

You can use the .php files as per the sample.
Rename the t2887.rar in the PHP to the name of the file you want to download.
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
PHP

From novice to tech pro — start learning today.