Force download with php file comes corrupted

Asked

Viewed 1,750 times

0

Hello, the code even downloads, the problem is that it is corrupted, and I have no idea why it happens.

The download is called so 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);

2 answers

3


The whole problem is that you are downloading along with the contents of 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>

Meaning when you download the file, it’s coming along:

<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 the 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 else 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 leave something more or less thus:

<?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

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

An alternative solution would be using the CURL (that returns in string and not in int), thus:

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);

2

I don’t know if that’s your situation:

when ordering the download instead of opening the "save as" window appears on the screen some strange characters like:

PK 劦 QJ 曷.arquivo.htmarquivo test zip PK 劦 QJ 曷. .htmPK9 archive?

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

Final recommendations:

  1. The sending of HTTP headers should always be done before any output to the browser, that is, always before HTML codes and functions that display data, such as echo, print, print_r etc. Nor should there be empty spaces in the code, before the tag <?php.

  2. Another detail is the BOM. BOM (Byte Order Mark) is a string that is inserted at the beginning of a file to define the order of bytes. That’s another reason for this problem.

  • It does not get any strange name, and the file is downloaded with the same size that was uploaded, however it comes corrupted with the following Winrar message Arquivo.zip. O arquivo está em formato desconhecido ou danificado.. Has no space before the tag <php? and not before the tag ?>, the code I use to force download is in the middle of other codes, do not know if this has problem or need are in a class alone.

  • Boy, what a situation. I found this somewhere http://www.yodot.com/pt/zip-reparar/archive-tampouco-corruer-ou-damaged.html

  • I looked, however I found nothing, even because I use php to do the compression, but when I finish compressing with php and open, the file works perfectly, the problem is when I force download.

  • I understand, I will not remove, for the time being, my answer, so that others in the community see these comments and can better understand their problem.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.