问题描述
代码如下:publicvoiddownFILE(Stringdest,OutputStreamos){FileInputStreamfis=null;try{fis=newFileInputStream(dest);byte[]buffer=newbyte[4096];intn;while((n=fis.read(buffer,0,buffer.length))!=-1){os.write(buffer,0,n);}}catch(Exceptione){}finally{try{fis.close();os.flush();os.close();}catch(IOExceptione){}}}上面入参分别是:dest:本地硬盘的一个ZIP文件(大小为2G)OS:为WEB请求的返回,response.getOutStream();请教一下CSDN的大神们,我有点疑问:上面是WEB页面中,用户点击一个导出链接后执行的后台文件流下载方法,如果是小文件没关系,大文件的话如何处理?现在这样的写法,大文件就直接报内存溢出的错误!再就是上面的缓冲区是不是失去意义了(如果是本地硬盘文件之间的IO,肯定有效果)这里通过WEB方式下载,缓冲区的概念是不是没用了?求大神们指点小弟一下,谢谢了!
解决方案
解决方案二:
用RandomAccessFile试试,参考下这个:packagecom.defonds.cds.common;importjava.io.BufferedOutputStream;importjava.io.File;importjava.io.IOException;importjava.io.OutputStream;importjava.io.RandomAccessFile;importjavax.servlet.ServletException;importjavax.servlet.http.HttpServlet;importjavax.servlet.http.HttpServletRequest;importjavax.servlet.http.HttpServletResponse;importorg.apache.commons.logging.Log;importorg.apache.commons.logging.LogFactory;importcom.defonds.cds.common.util.CommonUtil;//HTTP断点续传demo(客户端测试工具:快车、迅雷)publicclassArcSyncHttpDownloadServletextendsHttpServlet{privatestaticfinallongserialVersionUID=1L;finalstaticLoglog=LogFactory.getLog(ArcSyncHttpDownloadServlet.class);@OverrideprotectedvoiddoGet(HttpServletRequestreq,HttpServletResponseresp)throwsServletException,IOException{this.doPost(req,resp);}@OverrideprotectedvoiddoPost(HttpServletRequestrequest,HttpServletResponseresponse){FiledownloadFile=newFile("D:/defonds/book/pattern/SteveJobsZH.pdf");//要下载的文件longfileLength=downloadFile.length();//记录文件大小longpastLength=0;//记录已下载文件大小intrangeSwitch=0;//0:从头开始的全文下载;1:从某字节开始的下载(bytes=27000-);2:从某字节开始到某字节结束的下载(bytes=27000-39000)longtoLength=0;//记录客户端需要下载的字节段的最后一个字节偏移量(比如bytes=27000-39000,则这个值是为39000)longcontentLength=0;//客户端请求的字节总量StringrangeBytes="";//记录客户端传来的形如“bytes=27000-”或者“bytes=27000-39000”的内容RandomAccessFileraf=null;//负责读取数据OutputStreamos=null;//写出数据OutputStreamout=null;//缓冲byteb[]=newbyte[1024];//暂存容器if(request.getHeader("Range")!=null){//客户端请求的下载的文件块的开始字节response.setStatus(javax.servlet.http.HttpServletResponse.SC_PARTIAL_CONTENT);log.info("request.getHeader("Range")="+request.getHeader("Range"));rangeBytes=request.getHeader("Range").replaceAll("bytes=","");if(rangeBytes.indexOf('-')==rangeBytes.length()-1){//bytes=969998336-rangeSwitch=1;rangeBytes=rangeBytes.substring(0,rangeBytes.indexOf('-'));pastLength=Long.parseLong(rangeBytes.trim());contentLength=fileLength-pastLength+1;//客户端请求的是969998336之后的字节}else{//bytes=1275856879-1275877358rangeSwitch=2;Stringtemp0=rangeBytes.substring(0,rangeBytes.indexOf('-'));Stringtemp2=rangeBytes.substring(rangeBytes.indexOf('-')+1,rangeBytes.length());pastLength=Long.parseLong(temp0.trim());//bytes=1275856879-1275877358,从第1275856879个字节开始下载toLength=Long.parseLong(temp2);//bytes=1275856879-1275877358,到第1275877358个字节结束contentLength=toLength-pastLength+1;//客户端请求的是1275856879-1275877358之间的字节}}else{//从开始进行下载contentLength=fileLength;//客户端要求全文下载}/***如果设设置了Content-Length,则客户端会自动进行多线程下载。如果不希望支持多线程,则不要设置这个参数。*响应的格式是:*Content-Length:[文件的总大小]-[客户端请求的下载的文件块的开始字节]*ServletActionContext.getResponse().setHeader("Content-Length",*newLong(file.length()-p).toString());*/response.reset();//告诉客户端允许断点续传多线程连接下载,响应的格式是:Accept-Ranges:bytesresponse.setHeader("Accept-Ranges","bytes");//如果是第一次下,还没有断点续传,状态是默认的200,无需显式设置;响应的格式是:HTTP/1.1200OKif(pastLength!=0){//不是从最开始下载,//响应的格式是://Content-Range:bytes[文件块的开始字节]-[文件的总大小-1]/[文件的总大小]log.info("----------------------------不是从开始进行下载!服务器即将开始断点续传...");switch(rangeSwitch){case1:{//针对bytes=27000-的请求StringcontentRange=newStringBuffer("bytes").append(newLong(pastLength).toString()).append("-").append(newLong(fileLength-1).toString()).append("/").append(newLong(fileLength).toString()).toString();response.setHeader("Content-Range",contentRange);break;}case2:{//针对bytes=27000-39000的请求StringcontentRange=rangeBytes+"/"+newLong(fileLength).toString();response.setHeader("Content-Range",contentRange);break;}default:{break;}}}else{//是从开始下载log.info("----------------------------是从开始进行下载!");}try{response.addHeader("Content-Disposition","attachment;filename=""+downloadFile.getName()+""");response.setContentType(CommonUtil.setContentType(downloadFile.getName()));//settheMIMEtype.response.addHeader("Content-Length",String.valueOf(contentLength));os=response.getOutputStream();out=newBufferedOutputStream(os);raf=newRandomAccessFile(downloadFile,"r");try{switch(rangeSwitch){case0:{//普通下载,或者从头开始的下载//同1}case1:{//针对bytes=27000-的请求raf.seek(pastLength);//形如bytes=969998336-的客户端请求,跳过969998336个字节intn=0;while((n=raf.read(b,0,1024))!=-1){out.write(b,0,n);}break;}case2:{//针对bytes=27000-39000的请求raf.seek(pastLength-1);//形如bytes=1275856879-1275877358的客户端请求,找到第1275856879个字节intn=0;longreadLength=0;//记录已读字节数while(readLength<=contentLength-1024){//大部分字节在这里读取n=raf.read(b,0,1024);readLength+=1024;out.write(b,0,n);}if(readLength<=contentLength){//余下的不足1024个字节在这里读取n=raf.read(b,0,(int)(contentLength-readLength));out.write(b,0,n);}////raf.seek(pastLength);//形如bytes=1275856879-1275877358的客户端请求,找到第1275856879个字节//while(raf.getFilePointer()<toLength){//out.write(raf.read());//}break;}default:{break;}}out.flush();}catch(IOExceptionie){/***在写数据的时候,*对于ClientAbortException之类的异常,*是因为客户端取消了下载,而服务器端继续向浏览器写入数据时,*抛出这个异常,这个是正常的。*尤其是对于迅雷这种吸血的客户端软件,*明明已经有一个线程在读取bytes=1275856879-1275877358,*如果短时间内没有读取完毕,迅雷会再启第二个、第三个。。。线程来读取相同的字节段,*直到有一个线程读取完毕,迅雷会KILL掉其他正在下载同一字节段的线程,*强行中止字节读出,造成服务器抛ClientAbortException。*所以,我们忽略这种异常*///ignore}}catch(Exceptione){log.error(e.getMessage(),e);}finally{if(out!=null){try{out.close();}catch(IOExceptione){log.error(e.getMessage(),e);}}if(raf!=null){try{raf.close();}catch(IOExceptione){log.error(e.getMessage(),e);}}}}}
解决方案三:
其实基本也就这样,但是在循环里os.write(buffer,0,n);后面flush一下就好了
解决方案四:
大文件报溢出?真的么?为啥catch不输出异常?为啥你要在finally里面关os,os是这个函数开的么?为啥close都在一个try里?