7

I'm trying to download files via headers from my database. I'm not sure why my downloaded files are all corrupted when I change my download code to one that uses OOP but are fine when my code is non-OOP.

This is where I get the file id and call the download function:(handleDownload.php)

if (isset($_GET['id'])) {
        $id = $_GET['id'];
        //pump id into function getDBFiles to pull file with matching id
        $fileData = $Download->getDBFiles($id);
        header('Content-Type:"' . $fileData[2]. '"');
        header('Content-Disposition: attachment; filename="' . $fileData[1]. '"'); 
        echo $fileData[0];
        exit;
      }

This is the function that pulls the file from the database (download.php)

public function getDBFiles($id) {
            global $database;
            $sql = "SELECT * FROM ".self::$table_name." WHERE resume_id ='" . $id . "'";
            $result = $database->query($sql);
            if ($row = $result->fetch_array(MYSQLI_ASSOC)) {  
                $name = $row['resume_title'];
                $type = $row['file_type'];
                $content = $row['resume_data']; //content of file
                //$size = $row['file_size']; //file size
                return array($content, $name, $type);
            }
        }
 $Download = new Download();
 $download =& $Download;

The code works fine if it's all in one page as shown below though:

if (isset($_GET['id'])) {
    $id = $_GET['id'];
    mysqli_select_db($con, "apples");

    $query = "SELECT * FROM resume where resume_id ='" . $id . "'";
    $result = mysqli_query($con, $query) or die('Error, query failed');


    if ($row = $result->fetch_array(MYSQLI_ASSOC)) {
        $name = $row['resume_title'];
        $type = $row['file_type'];
        $content = $row['resume_data']; //content of file
        $size = $row['file_size']; //file size
        header('Content-Type:"' . $type . '"');
        //header('Content-length:"' . $size . '"');
        header('Content-Disposition: attachment; filename="' . $name . '"');
        //var_dump($row);
        echo $content;
    }
}

UPDATE: I'm now getting a download file is corrupted instead of a blank file. This is how the same file is outputted by the different download codes. The one on top is from the OOP code while the other is from the working non-OOP version. hex comparison

This is my download code in its entirety.

try {
    //execute retrieval of files from database
    $Download-> showDBFiles();
    //pass results to output array 
    $output = $Download->getMessages();
    //if id is set then get file from database
    if (isset($_GET['id'])) {
        $id = $_GET['id'];
        //pump id into function getDBFiles to pull file with matching id
        $fileData = $Download->getDBFiles($id);
        header('Content-Type:"' . $fileData[2]. '"');
        header('Content-Disposition: attachment; filename="' . $fileData[1]. '"'); 
        echo $fileData[0];
        die();
      }
} catch (Exception $e) {
    $result[] = $e->getMessages();
}

After calling the functions, I would then echo out the output (the download links) with a foreach loop

<h2>Output</h2>
<?php if ($output) { ?>
<ul class="result">
    <?php
    foreach ($output as $message) {
        $id = $message['id'];
        $name = $message['name'];
        ?>
    <li><a href="handleDownload.php?id=<?php echo $id; ?>"><?php echo $name; ?></a></li>

    <?php }
?>
</ul>
yivi
  • 42,438
  • 18
  • 116
  • 138
  • The first one $row is NULL? – SuperBear Aug 02 '15 at 09:59
  • Do you mean the one with OOP? If so, nope it's not NULL. I'm able to get the following vardump from it: array(8) { ["resume_id"]=> string(2) "83" ["individual_id"]=> string(1) "7" ["resume_title"]=> string(15) "hoppingBoy.docx" ["file_type"]=> string(30) "application/vnd.openxmlformats" ["file_size"]=> string(6) "215908" ["upload_date"]=> string(10) "2015-08-02" ["status"]=> string(1) "1" ["resume_data"]=> string(215908)...} – Darrion Rockxmylife Aug 02 '15 at 10:03
  • 2
    **Danger**: You are **vulnerable to [SQL injection attacks](http://bobby-tables.com/)** that you need to [defend](http://stackoverflow.com/questions/60174/best-way-to-prevent-sql-injection-in-php) yourself from. – Quentin Aug 02 '15 at 10:09
  • @Quentin Thanks for the heads up I'll definitely look into preventing SQL injection attacks later. Right now though, I'm struggling to even get it to work! – Darrion Rockxmylife Aug 02 '15 at 10:14
  • @DarrionRockxmylife it is weird. Try to move headers from method. For example: class Download { public function getDBFiles($id) { /* your code */ return array($content, $name, $type); } }. On your page: $fileData = $Download->getDBFiles($id); header('Content-Type:"' . $fileData[2]. '"'); header('Content-Disposition: attachment; filename="' . $fileData[1]. '"'); echo $fileData[0]; – Danila Ganchar Aug 02 '15 at 10:16
  • @DanilaGanchar I've followed your advice and I still can't get it to work. I'm getting a file is corrupted error now. (I've updated my codes to show the changes made) – Darrion Rockxmylife Aug 02 '15 at 10:47
  • @DarrionRockxmylife can you show $fileData? I mean: var_dump($fileData); die(); – Danila Ganchar Aug 02 '15 at 10:52
  • @DanilaGanchar Sorry for the late reply! This was what I got: array(3) { [0]=> string(16347) ".../binary data/..." [1]=> string(15) "testing456.docx" [2]=> string(30) "application/vnd.openxmlformats" } – Darrion Rockxmylife Aug 02 '15 at 11:21
  • @DarrionRockxmylife hm. strangely. I see that in first example you use global $database; but below: mysqli_select_db($con, "apples"); may be problem in data? – Danila Ganchar Aug 02 '15 at 11:31
  • `$fileData` is the same with both ways you tried? The binary content is *exactly* the same? When you download the file and compare its content (e.g., via a hex editor), is this content *exactly* the same? – stef77 Aug 02 '15 at 14:48
  • @stef77 No, I initially did not have $fileData at handleDownload.php as my headers were at download.php so I did not have to return the array. Hmmm, it appears they are not. It seems like my code with OOP is giving me an output that is in ANSCI while the non-OOP working version is giving me one that is in UNICODE – Darrion Rockxmylife Aug 03 '15 at 01:08
  • @DanilaGanchar Hmm, I don't think $global is the issue. I'm just using it to access the database from another php page – Darrion Rockxmylife Aug 03 '15 at 01:09
  • @DarrionRockxmylife check encoding of files. May be your class not use utf-8? About connection to mysql... Are you sure that in 2 examples you use the same database and table? – Danila Ganchar Aug 03 '15 at 08:04
  • 1
    Interestingly, in your update, you seem to be getting the correct data, but in the form of a hexdump (which you are then examining itself in a hex editor) and minus the UTF-16 BOM (0xFEFF) at the start of the data stream. _Hex values_ of the first 4 non-zero bytes after BOM in correct file: `50 4b 03 04` First few _characters_ after the address offset 00000 (viewable in the right hand pane of the top window): `504b0304` Are you doing any processing after fetching from the DB? – Benjamin Aug 03 '15 at 10:33
  • @DanilaGanchar Yes I'm confident they are from the same database and table – Darrion Rockxmylife Aug 03 '15 at 11:25
  • @Benjamin No, after calling the getDBFiles() function I would just use a foreach loop to echo my output onto the page. (I've added my whole code to my answer above) – Darrion Rockxmylife Aug 03 '15 at 11:27
  • I suspect that you are attempting to use different character sets in your connections to the DB - please use the functions described [here](http://php.net/manual/en/mysqli.get-charset.php) in order to dump the charset for each of the (oop and non-oop) connections, and compare. – Benjamin Aug 03 '15 at 13:38
  • Hm. What's `$database`, and can you show its `query` function? This is very strange, the `00000000`, `00000010`, ... things... Can't imagine where they come from... You downloaded the files and then opened them in the hex editor, nothing else? – stef77 Aug 03 '15 at 18:47

1 Answers1

1

In your non-OOP solution check for leading whitespaces inside the php file.

The following code would produce a corrupted file because of a leading whitespace.

 <?php
if (isset($_GET['id'])) {...

This also applies to whitespaces after the closing php tag (which you should not use).

Those chars will be submitted by your browser, included in the download stream and corrupt the whole file.

cb0
  • 8,415
  • 9
  • 52
  • 80