我遇到了像这样的senerior:
我的项目有一个servlet来捕获来自perl的请求。请求是下载文件。请求是multipartRequest。
@RequestMapping(value = "/*", method = RequestMethod.POST)
public void tdRequest(@RequestHeader("Authorization") String authenticate,
HttpServletResponse response,
HttpServletRequest request) throws Exception
{
if (ServletFileUpload.isMultipartContent(request))
{
ServletFileUpload sfu = new ServletFileUpload();
FileItemIterator items = sfu.getItemIterator(request);
while (items.hasNext())
{
FileItemStream item = items.next();
if (("action").equals(item.getFieldName()))
{
InputStream stream = item.openStream();
String value = Streams.asString(stream);
if (("upload").equals(value))
{
uploadRequest(items, response);
return;
}
else if (("download").equals(value))
{
downloadRequest(items, response);
return;
}
问题不在这里,它出现在downloadRequest()函数中。
void downloadRequest(FileItemIterator items,
HttpServletResponse response) throws Exception
{
log.info("Start downloadRequest.......");
OutputStream os = response.getOutputStream();
File file = new File("D:\\clip.mp4");
FileInputStream fileIn = new FileInputStream(file);
//while ((datablock = dataOutputStreamServiceImpl.readBlock()) != null)
byte[] outputByte = new byte[ONE_MEGABYE];
while (fileIn.read(outputByte) != -1)
{
System.out.println("--------" + (i = i + 1) + "--------");
System.out.println(new Date());
//dataContent = datablock.getContent();
System.out.println("Start write " + new Date());
os.write(outputByte, 0,outputByte.length);
System.out.println("End write " + new Date());
//System.out.println("----------------------");
}
os.close();
}
}
我尝试从文件中读取和写入1MB的块。但是,下载整个文件需要很长时间。 (我的情况是20分钟,文件为100MB)
我尝试使用sysout,我看到了这样的结果:
前几个块可以快速读取,写入数据:
--------1--------
Mon Dec 07 16:24:20 ICT 2015
Start write Mon Dec 07 16:24:20 ICT 2015
End write Mon Dec 07 16:24:21 ICT 2015
--------2--------
Mon Dec 07 16:24:21 ICT 2015
Start write Mon Dec 07 16:24:21 ICT 2015
End write Mon Dec 07 16:24:21 ICT 2015
--------3--------
Mon Dec 07 16:24:21 ICT 2015
Start write Mon Dec 07 16:24:21 ICT 2015
End write Mon Dec 07 16:24:21 ICT 2015
但是下一个块比前一个块慢
--------72--------
Mon Dec 07 16:29:22 ICT 2015
Start write Mon Dec 07 16:29:22 ICT 2015
End write Mon Dec 07 16:29:29 ICT 2015
--------73--------
Mon Dec 07 16:29:29 ICT 2015
Start write Mon Dec 07 16:29:29 ICT 2015
End write Mon Dec 07 16:29:37 ICT 2015
--------124--------
Mon Dec 07 16:38:22 ICT 2015
Start write Mon Dec 07 16:38:22 ICT 2015
End write Mon Dec 07 16:38:35 ICT 2015
--------125--------
Mon Dec 07 16:38:35 ICT 2015
Start write Mon Dec 07 16:38:35 ICT 2015
End write Mon Dec 07 16:38:48 ICT 2015
问题出在os.write()中 我真的无法理解outputStream是如何写的,为什么需要这么长时间呢?或者我犯了一些错误?
抱歉我的英语不好。我真的需要你的支持。提前谢谢!
这是来自客户端的perl代码
# ----- get connected to download the file
#
$Response = $ua->request(POST $remoteHost ,
Content_Type => 'form-data',
Authorization => $Authorization,
'Proxy-Authorization' => $Proxy_Authorization ,
Content => [ DOS => 1 ,
action => 'download' ,
first_run => 0 ,
dl_filename => $dl_filename ,
delivery_dir => $delivery_dir ,
verbose => $Verbose ,
debug => $debug ,
version => $VERSION
]
);
unless ($Response->is_success) {
my $Msg = $Response->error_as_HTML;
# Remove HTML tags - we're in a DOS shell!
$Msg =~ s/<[^>]+>//g;
print "ERROR! SERVER RESPONSE:\n$Msg\n";
print "$remoteHost\n\n" if $Options{'v'};
Error "Could not connect to " . $remoteHost ;
}
my $Result2 = $Response->content();
Error "Abnormal termination...\n$Result2" if $Result2 =~ /_APP_ERROR_/;
open(F, ">$dl_filename") or Error "Could not open '$dl_filename'!";
binmode F; # unless $dl_filename =~ /\.txt$|\.htm$/;
print F $Result2;
close F;
print "received.\n";
}
答案 0 :(得分:2)
一个问题是fileIn.read(outputByte)可以读取随机字节数,而不仅仅是完整的outputByte。你读了几KB,然后你存储了1MB,而且你的磁盘空间不足。试试这个,注意“readed”参数。
void downloadRequest(FileItemIterator items,
HttpServletResponse response) throws Exception
{
log.info("Start downloadRequest.......");
OutputStream os = response.getOutputStream();
File file = new File("D:\\clip.mp4");
FileInputStream fileIn = new FileInputStream(file);
//while ((datablock = dataOutputStreamServiceImpl.readBlock()) != null)
byte[] outputByte = new byte[ONE_MEGABYE];
int readed =0;
while ((readed =fileIn.read(outputByte)) != -1)
{
System.out.println("--------" + (i = i + 1) + "--------");
System.out.println(new Date());
//dataContent = datablock.getContent();
System.out.println("Start write " + new Date());
os.write(outputByte, 0,readed );
System.out.println("End write " + new Date());
//System.out.println("----------------------");
}
os.close();
}
}
答案 1 :(得分:2)
看起来您的下载性能越来越慢,您进入下载的距离越远。你可以在每个块的一个或更少的时间内开始,在块72中,每个块为7秒以上,在块128中,每个块为13秒。
服务器端没有任何内容可以解释这一点。相反,它有客户端的“气味”做错了。我的猜测是客户端正在从套接字读取数据到内存中的数据结构,并且该数据结构(可能只是String
或StringBuffer
或StringBuilder
)正在获取越来越大。要么扩大它的时间越来越大,或者你的内存占用越来越大,GC的使用时间越来越长。 (或两者兼而有之。)
如果您向我们展示了客户端代码.....
<强>更新强>
正如我所怀疑的那样,这行代码将把整个内容读入字符串构建器的Perl等效内容,然后再将其转换为字符串。
my $Result2 = $Response->content();
根据它在引擎盖下的实现方式,当构建器耗尽缓冲区空间并需要扩展时,这将导致重复复制数据。根据Perl为此采用的缓冲区扩展策略,它可能会产生O(N^2)
行为,其中N
是您要传输的文件的大小。 (证据表明你没有得到O(N)
行为......)
如果您想要更快的下载,则需要在客户端流式传输数据。以块的形式读取响应内容并将其写入输出文件。 (我不是Perl专家,因此我无法为您提供代码。)这也会减少客户端的内存占用量......如果文件大小增加,这可能很重要。