Как загрузить двоичные файлы в mod_perl с CGI.pm? - PullRequest
1 голос
/ 19 июня 2011

У меня есть большой кусок производственного кода, который работает.Но после того, как я настроил новую среду на виртуальной машине, у меня возникла одна проблема - каждый раз, когда мне нужно было загрузить двоичный файл, он запутывался с преобразованиями в Юникод.

Таким образом, есть подпрограмма, где проблема:

sub save_uploaded_file
{
    # $file is obtained by param(zip) 
    my ($file) = @_;
    my ($fh, $fname) = tmpnam;
    my ($br, $buffer);
    # commenting out next 2 lines doesn't help either
    binmode $file, ':raw';
    binmode $fh, ':raw';
    while ($br = sysread($file, $buffer, 16384))
    {
        syswrite($fh, $buffer, $br);
    }
    close $fh;
    return $fname;
}

Он используется для загрузки zip-архивов, но они загружены как искаженные (их размер всегда больше, чем в оригинале), и я заглянул внутрь них с помощью шестнадцатеричного редактора и обнаружил, что есть много заменяющих символов Юникода, закодированныхв utf-8, внутри (EF BF BD).

Я выяснил, что общая сумма прочитанных байтов больше, чем в исходном файле.Таким образом, проблема начинается с sysread.

Текстовые файлы загружаются хорошо.

Обновление : есть двоичное представление первых нескольких байтов передаваемого файла:

0000000: 504b 0304 1400 0000 0800 efbf bd1c efbf  PK..............
0000010: bd3e efbf bd1d 3aef bfbd efbf bd02 0000  .>....:.........
0000020: efbf bd05 0000 0500 1c00 422e 786d 6c55  ..........B.xmlU
0000030: 5409 0003 5cef bfbd efbf bd4d 18ef bfbd  T...\......M....
0000040: efbf bd4d 7578 0b00 0104 efbf bd03 0000  ...Mux..........
0000050: 0404 0000 00ef bfbd efbf bdef bfbd 6bef  ..............k.

И оригинал:

0000000: 504b 0304 1400 0000 0800 b81c d33e df1d  PK...........>..
0000010: 3aa0 8102 0000 a405 0000 0500 1c00 422e  :.............B.
0000020: 786d 6c55 5409 0003 5cd4 fc4d 18c7 fc4d  xmlUT...\..M...M
0000030: 7578 0b00 0104 e803 0000 0404 0000 008d  ux..............
0000040: 94df 6bdb 3010 c7df 03f9 1f0e e1bd 254e  ..k.0.........%N
0000050: ec74 6c85 d825 2bac 9442 379a c25e ca8a  .tl..%+..B7..^..

Обновление2 Работающее программное обеспечение - centos 5.6, perl 5.8.8, apache 2.2.3

Ответы [ 4 ]

0 голосов
/ 09 октября 2013

У меня было, как мне кажется, такая же проблема. Ошибка, казалось, возникала очень рано, потому что ни один из моего кода никогда не выполнялся, когда клиент пытался загрузить двоичный файл. Я исправил это, установив STDIN в «raw» (двоичный файл), в верхней части скрипта…

binmode (STDIN, ': raw');

0 голосов
/ 19 июня 2011

Насколько я знаю, Perl 5 не меняет заменяющий символ ни в одном из своих слоев io. Это только те преобразования, о которых я знаю, это преобразования новой строки (то есть текстовый слой). Вы уверены, что исходный файл не содержит эти последовательности байтов?

Этот код работает для меня, он работает для вас?

#!/usr/bin/perl

use strict;
use warnings;

use File::Temp qw/:POSIX/;

sub save_uploaded_file {
    # $file is obtained by param(zip) 
    my ($file) = @_;
    my ($fh, $fname) = tmpnam;
    my ($br, $buffer);
    # commenting out next 2 lines doesn't help either
    binmode $file, ':raw'
        or die "could not change input file to raw: $!";
    binmode $fh, ':raw'
        or die "could not change tempfile to raw: $!";
    while ($br = sysread($file, $buffer, 16384)) {
        syswrite($fh, $buffer, $br);
    }
    close $fh
        or die "could not close tempfile: $!";
    return $fname;
}

sub check {
    my $input_file = shift;

    print "$input_file is ", -s $input_file, " bytes long\n"; 

    open my $fh, "<:raw", $input_file
        or die "could not open $input_file for reading: $!";

    my $bytes = sysread $fh, my $buf, 4096;

    print "read $bytes bytes: ", 
        join(", ", map { sprintf "%02x", $_ } unpack "C*", $buf),
        "\n";
}

my $input_file = "test.bin";

open my $fh, ">:raw", $input_file
    or die "could not open $input_file for writing: $!";

print $fh pack "CC", 0xFF, 0xFD
    or die "could not write to $input_file: $!";

close $fh
    or die "could not close $input_file: $!";

check $input_file;

open my $newfh, "<", $input_file
    or die "could not open $input_file: $!";
my $new_file = save_uploaded_file $newfh;

check $new_file;
0 голосов
/ 19 июня 2011

sysread читает файл как utf8, но файл не utf8!первые десять байтов находятся в «базовом латинском диапазоне» (00-7F), поэтому они интерпретируются как один и тот же байт.Следующий байт 'b8' находится вне допустимого диапазона и заменяется на 'efbfbd' <=> \ x {FFFD} (специальный символ, указывающий на ошибку декодирования).Все байты больше 7F заменяются на \ x {FFFD}.

Какую версию perl и ОС вы используете?Есть отчет (ошибка perl 75106) с заголовком binmode $fh, ":raw" doesn't undo :utf8 on win32!

0 голосов
/ 19 июня 2011

Возвращает ли tmpnam дескриптор файла, помеченный как utf8? Я думаю нет!

попробуй binmode $fh, ":utf8" ;

...