Force download with php file comes corrupted

0

Hello, the code even comes to download, the problem is that it comes corrupted, and I have no idea why this happens.

The download is called site.com/download/key

.htaccess:

RewriteEngine On
RewriteBase /
RewriteCond %{SCRIPT_FILENAME} !-f
RewriteCond %{SCRIPT_FILENAME} !-d

RewriteRule (.*) /index.php [QSA,L]

index.php

<?php
require 'config/Config.php';
require 'config/tratarUrl.php';
?>
<html lang="pt-br">
    <link rel="stylesheet" href="<?=DIR_CSS?>Style.css">
    <link rel="icon" href="<?= DIR_IMAGES?>icon.png"
    <meta charset="UTF-8">
    <title>Site</title>
    <head>
        <meta charset="UTF-8">
    </head>
    <body>
        <?php
            include $pag;
        ?>
    </body>
</html>

download.php

$file_path = DIR_ARQUIVOS.$key.'.zip';
$file_name = $key.'.zip';
$new_name = $sql->getKeyName($key);

set_time_limit(0);

// Verifica se o arquivo não existe
if (!file_exists($file_path)) {
// Exiba uma mensagem de erro caso ele não exista
exit;
}

// Configuramos os headers que serão enviados para o browser
header('Content-Type: "application/zip"');
header('Content-Disposition: attachment; filename="'.basename($file_path).'"');
header("Content-Transfer-Encoding: binary");
header('Expires: 0');
header('Pragma: no-cache');
// Envia o arquivo para o cliente
readfile($file_name);
    
asked by anonymous 24.03.2017 / 13:17

2 answers

3

The whole problem is that you're downloading content from index.php together:

<?php
require 'config/Config.php';
require 'config/tratarUrl.php';
?>
<html lang="pt-br">
    <link rel="stylesheet" href="<?=DIR_CSS?>Style.css">
    <link rel="icon" href="<?= DIR_IMAGES?>icon.png"
    <meta charset="UTF-8">
    <title>Site</title>
    <head>
        <meta charset="UTF-8">
    </head>
    <body>
        <?php
            include $pag;
        ?>
    </body>
</html>

That is when you download the file, this is coming together:

<html lang="pt-br">
    <link rel="stylesheet" href="Style.css">
    <link rel="icon" href="icon.png">
    <meta charset="UTF-8">
    <title>Site</title>
    <head>
        <meta charset="UTF-8">
    </head>
    <body>
        CONTEUDO DO ARQUIVO AQUI
    </body>
</html>

I recommend separating download.php or simply creating an if.

Using if:

<?php
require 'config/Config.php';
require 'config/tratarUrl.php';

if (preg_match('#pages/download\.php$#', $pag) > 0) {
     include $pag;
     exit;
}
?>
<html lang="pt-br">
    <link rel="stylesheet" href="<?=DIR_CSS?>Style.css">
    <link rel="icon" href="<?= DIR_IMAGES?>icon.png"
    <meta charset="UTF-8">
    <title>Site</title>
    <head>
        <meta charset="UTF-8">
    </head>
    <body>
        <?php
            include $pag;
        ?>
    </body>
</html>

Or you can rewrite .htaccess like this:

RewriteEngine On
RewriteBase /
RewriteCond %{SCRIPT_FILENAME} !-f
RewriteCond %{SCRIPT_FILENAME} !-d

#se a url vier com prefix download
RewriteRule ^download/ /pages/download.php [QSA,L]

#outras urls
RewriteRule (.*) /index.php [QSA,L]

Then your pages/download.php should get something more or less like this:

<?php
require 'config/Config.php';

//... Conteudo do seu download.php ...

$file_path = DIR_ARQUIVOS.$key.'.zip';
$file_name = $key.'.zip';
$new_name = $sql->getKeyName($key);

set_time_limit(0);

// Verifica se o arquivo não existe
if (!file_exists($file_path)) {
// Exiba uma mensagem de erro caso ele não exista
exit;
}

// Configuramos os headers que serão enviados para o browser
header('Content-Type: "application/zip"');
header('Content-Disposition: attachment; filename="'.basename($file_path).'"');
header("Content-Transfer-Encoding: binary");
header('Expires: 0');
header('Pragma: no-cache');
// Envia o arquivo para o cliente
readfile($file_name);

Problems with filesize

filesize was not the problem of your script, but the mixed content as I mentioned, an interesting thing to send Content-Length is that the browsers progress bar usually give an estimate of the percentage of the most accurate download , then it would be interesting to define this. However it is worth noting that filesize uses int and depending on the processor architecture you will have limits, which can cause problems like what I mentioned in filesize for files larger than 2GB on x86 platforms

A workaround would be to use CURL (which returns string and not int ), like this:

function filesizecurl($arquivo)
{
    if (is_file($arquivo) === false) {
        return false;
    }

    $arquivo = realpath(preg_replace('#^file:#', '', $arquivo));

    $ch = curl_init('file://' . ltrim($arquivo, '/'));

    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); //Faz o retorno ser salvo na variável
    curl_setopt($ch, CURLOPT_HEADER, 1); //Faz retornar os headers
    curl_setopt($ch, CURLOPT_NOBODY, 1); //Evita retornar o corpo

    $headers = curl_exec($ch);
    curl_close($ch);

    $ch = null;

    //Com preg_match extraímos o tamanho retornado de Content-Length
    if (preg_match('#(^c|\sc)ontent\-length:(\s|)(\d+)#i', $headers, $matches) > 0) {
        return $matches[3];
    }

    return false;
}

// Configuramos os headers que serão enviados para o browser
header('Content-Length: ' . filesizecurl($file_name));
header('Content-Type: "application/zip"');
header('Content-Disposition: attachment; filename="'.basename($file_path).'"');
header("Content-Transfer-Encoding: binary");
header('Expires: 0');
header('Pragma: no-cache');
// Envia o arquivo para o cliente
readfile($file_name);
    
24.03.2017 / 17:44
2

I do not know if this is your situation:

When requesting the download instead of opening the "save as" window, some strange characters will appear on the screen:

PK 劦 QJ 曷 .file.http test file PK 劦 QJ 曷. file.htmPK9?

If this is the case then you should check that before the php open tag is blanks, if there are any, remove them so that the first line is <?php or that line 1 should start with the opening of php

  

Final considerations:

  • The sending of HTTP headers must always be done before any browser output, ie always before HTML codes and functions that display data, such as echo, print, print_r etc. There can also be no empty spaces in the code, before the <?php tag.

  • Another detail is BOM. The BOM (Byte Order Mark) is a sequence of characters that is inserted at the beginning of a file to define the order of bytes. This is another reason for this problem.

  • 24.03.2017 / 14:42