flamey
flamey

Reputation: 2399

First 8 bytes are always wrong when downloading a file from my script

I have a Mojolicious Lite script that "gives out" an executable file (user can download the file from the script's URL). I keep encoded data in an inline template in DATA section, then encode it and render_data.

get '/download' => sub {
    my $self = shift;

    my $hex_data = $self->render_partial( 'TestEXE' );
    my $bin_data;
    while( $hex_data =~ /([^\n]+)\n?/g ) {
        $bin_data .= pack "H".(length $1), $1;
    }

    my $headers = Mojo::Headers->new; 
    $headers->add( 'Content-Type', 'application/x-download;name=Test.exe' );
    $headers->add( 'Content-Disposition', 'attachment;filename=Test.exe' );
    $headers->add( 'Content-Description', 'File Transfer');

    $self->res->content->headers($headers);
    $self->render_data( $bin_data ); 
};

__DATA__

@@ TestEXE.html.ep
4d5a90000300000004000000ffff0000b8000000000000004000000000000000
00000000000000000000000000000000000000000000000000000000b0000000
0e1fba0e00b409cd21b8014ccd21546836362070726f6772616d2063616e6e6f
....

When I run this locally (via built in webserver on http://127.0.0.1:3000/, Win7) I get the correct file (size and contents). But when I run it in CGI mode on shared hosting (Linux), it comes back with correct size, but first 8 bytes of the file are always incorrect (and always different). The rest of the file is correct.

If in my sub i specify $hex_data instead of $bin_data I get what suppose to be there.

I'm at lost.

Upvotes: 4

Views: 353

Answers (1)

Tempire
Tempire

Reputation: 2328

render_partial isn't what you want.

First, re-encode the executable in base64 format, and specify that the template is base64 encoded (This is assuming hex is not a requirement for your app):

@@ template-name (base64)

Also, you don't actually need a controller method at all. Mojolicious will handle the process for you - all you have to do is appropriately name the template.

use Mojolicious::Lite;

app->start;

__DATA__

@@ Test.exe (base64)
...

http://127.0.0.1:3000/Test.exe will then download the file.

-

If you still want to use a controller method for app-specific concerns, get the data template specifically:

use Mojolicious::Lite;

get '/download' => sub {
    my $self = shift;

    # http://mojolicio.us/perldoc/Mojolicious/Renderer.pm#get_data_template
    my $data = $self->app->renderer->get_data_template({}, 'Test.exe');

    # Replace content-disposition instead of adding it, 
    # to prevent duplication from elsewhere in the app
    $self->res->headers->header(
        'Content-Disposition', 'attachment;filename=name.exe');

    $self->render_data($data);
};

app->start;

__DATA__

@@ Test.exe (base64)
...

http://127.0.0.1:3000/download will get the template, set the header, and then download it as name.exe.

Upvotes: 5

Related Questions