0

I'm currently trying to download a blob archive file saved in the database

$fid = $_GET['fid'];

$sel = $wpdb->get_results( "SELECT * FROM uploads WHERE id = $fid" );
foreach( $sel as $head )
{
    $name = $head->name;
    $type = $head->type;
    $size = $head->size;
    $content = $head->content;

    header("Content-length: $size");
    header("Content-type: $type");
    header("Content-Transfer-Encoding: Binary");
    header("Content-Disposition: attachment; filename=$name");
    echo $content;
    exit;
}

every time I redirect to /download?id= with int value assigned to id it loads the file. However, instead of the browser prompting for file download location, the page reads the binary contents of the blob and displays it all as a string.

any other way of downloading a blob on wordpress?

1
  • When/where do you do this? What action is this hooked to?
    – Milo
    Sep 4, 2017 at 15:03

2 Answers 2

2

There are some security issues. Please sanitize your input because someone can download from Your database something different than archive, or simply drop Your database.

$fid = sanitize_text_field( $_GET['fid'] );

1
  • thanks for the reminder! but no worries I review my codes multiple times for security reasons Sep 4, 2017 at 12:54
0

Use the following content-type header in your code:

header( "Content-type: application/octet-stream" );

If that doesn't work - we successfully do this and though it is unlikely, it may be related to the order of things. Try it in the following order:

Content-type
Content-disposition
Content-Transfer-Encoding
Content-Length

And as was mentioned in the other answer - always sanitize input before using it directly in database queries.

Edited: Also add to ensure there's no caching (taken from here)-

header('Connection: Keep-Alive');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Pragma: public');

Edited: Wrap up the code into an function to run at 'init' so it's not output in the theme content.

add_action(
    'init',
    function () {
        global $wpdb;

        $fid = sanitize_text_field( $_GET[ 'fid' ] );

        $sel = $wpdb->get_results( "SELECT * FROM uploads WHERE id = $fid" );
        foreach ( $sel as $head ) {
            $name = empty( $head->name ) ? 'myfile.name' : $head->name;
            $size = empty( $head->size ) ? strlen( $head->content ) : $head->size;

            header( 'Content-Type: application/octet-stream' );
            header( "Content-Disposition: attachment; filename=$name" );
            header( 'Content-Transfer-Encoding: Binary' );
            header( "Content-Length: $size" );
            header( 'Connection: Keep-Alive' );
            header( 'Expires: 0' );
            header( 'Cache-Control: must-revalidate, post-check=0, pre-check=0' );
            header( 'Pragma: public' );

            echo $head->content;

            exit();
        }
    }
);
10
  • still didn't work.. here's the output Sep 4, 2017 at 14:15
  • And you replaced your line header( "Content-type: with my one?
    – Paul G.
    Sep 4, 2017 at 14:20
  • yep! already did Sep 4, 2017 at 14:26
  • I added a couple of other headers you could add. Also, your server might be caching the response it sends out which means you always get the same response. So to test, make sure you add a new random number to your testing query. So, for example, you have asdfasdf.com?fid=1&rand=123 Each time you test it, change your rand number parameter
    – Paul G.
    Sep 4, 2017 at 14:42
  • made me think maybe it's the theme doing this? it's displaying the file contents rather than prompting for a save file location Sep 4, 2017 at 15:14

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.