我有一大块生产代码,有效。但是在我在虚拟机中设置一个新环境后,我遇到了一个问题 - 每次我需要上传一个二进制文件时,它都会变成乱码转换。
所以有一个sub,问题是:
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档案,但它们上传为格式不正确(它们的大小总是比原始大)我用十六进制编辑器查看它们内部,发现有很多unicode替换字符,用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..^..
UPDATE2 正在运行的软件是centos 5.6,perl 5.8.8,apache 2.2.3
答案 0 :(得分:0)
tmpnam
是否返回标记为utf8的文件句柄?我想不是!
尝试binmode $fh, ":utf8" ;
答案 1 :(得分:0)
据我所知,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;
答案 2 :(得分:0)
sysread正在以utf8的形式读取文件,但该文件不是utf8!前十个字节在“基本拉丁范围”(00-7F)中,因此它们被解释为相同的字节。下一个字节'b8'不在有效范围内,并且被'efbfbd'&lt; =&gt;取代。 \ x {FFFD}(表示解码错误的特殊字符)。 所有大于7F的字节都被\ x {FFFD}替换。
您使用的perl版本和操作系统是什么?
有一个标题为binmode $fh, ":raw" doesn't undo :utf8 on win32
的报告(perl bug 75106)!
答案 3 :(得分:0)
我认为是同样的问题。错误似乎很早就发生了,因为当客户端尝试加载二进制文件时,我的代码都没有执行过。我通过在脚本的顶部将 STDIN 设置为“raw”(二进制)来修复它...
binmode(STDIN,':raw');