Servlet 3.0筆記之文件下載的那點事情
使用Servlet 3.0 提供文件下載,當然了任何Servlet版本,都可以做到,這里僅僅作為知識的積累。下面貼出代碼,防止忘卻。
一。常規(guī)方式文件下載示范
很普通,簡單易用,不多說。一般情況下,夠用了。
二。偽零拷貝(zero copy)方式文件下載示范
變化不大,關(guān)鍵代碼在于:利用
FileChannel.transferTo(long position, long count, WritableByteChannel target)方法達到零拷貝(zero copy)目的。把HttpServletResponse的輸出對象(ServletOutputStream)利用Channels.newChannel(OutputStream out)工具,構(gòu)建一個WritableByteChannel對象而已。
OutputStream out = response.getOutputStream();
WritableByteChannel outChannel = Channels.newChannel(out);
測試代碼:
心存疑慮的是,這個是偽零拷貝方式實現(xiàn)。查看一下Channels.newChannel的源碼:
public static WritableByteChannel newChannel(final OutputStream out) {因為輸入的方法參數(shù)為ServletOutputStream類型實例,因此只能返回一個新構(gòu)建的WritableByteChannelImpl對象。具體構(gòu)建:
if (out == null) {
throw new NullPointerException();
}
if (out instanceof FileOutputStream &&
FileOutputStream.class.equals(out.getClass())) {
return ((FileOutputStream)out).getChannel();
}
return new WritableByteChannelImpl(out);
}
private static class WritableByteChannelImpl很顯然,也是屬于內(nèi)存類型的拷貝了,只能算作偽零拷貝實現(xiàn)了。
extends AbstractInterruptibleChannel // Not really interruptible
implements WritableByteChannel
{
OutputStream out;
private static final int TRANSFER_SIZE = 8192;
private byte buf[] = new byte[0];
private boolean open = true;
private Object writeLock = new Object();
WritableByteChannelImpl(OutputStream out) {
this.out = out;
}
public int write(ByteBuffer src) throws IOException {
int len = src.remaining();
int totalWritten = 0;
synchronized (writeLock) {
while (totalWritten < len) {
int bytesToWrite = Math.min((len - totalWritten),
TRANSFER_SIZE);
if (buf.length < bytesToWrite)
buf = new byte[bytesToWrite];
src.get(buf, 0, bytesToWrite);
try {
begin();
out.write(buf, 0, bytesToWrite);
} finally {
end(bytesToWrite > 0);
}
totalWritten += bytesToWrite;
}
return totalWritten;
}
}
protected void implCloseChannel() throws IOException {
out.close();
open = false;
}
}
三。轉(zhuǎn)發(fā)到文件服務(wù)器上
一般常識為,讓最擅長的人來做最擅長的事情,是為高效。使用類如Nginx高效的Web服務(wù)器專門處理文件下載業(yè)務(wù),達到零拷貝的目的,也是最佳搭配組合。Nginx可以利用header元數(shù)據(jù)X-Accel-Redirect來控制文件下載行為,甚是不錯。利用JAVA進行業(yè)務(wù)邏輯判斷,若符合規(guī)則,則提交給Nginx進行處理文件的下載,否則,返回給終端用戶權(quán)限不夠等信息。
用于控制用戶是否具有資格進行文件下載業(yè)務(wù)的控制器:
當然,這個僅僅用于演示,邏輯簡單。因為需要和nginx服務(wù)器進行配合,構(gòu)建一個Server,其配置文件:
我們在nginx配置文件中,設(shè)置/dowloads/目錄是不允許直接訪問的,必須經(jīng)由/download/控制器進行轉(zhuǎn)發(fā)方可。經(jīng)測試,中文名不會出現(xiàn)亂碼問題,保存的文件也是我們所請求的文件,同名,也不會出現(xiàn)亂碼問題。但是,若在后臺獲取文件名,用于顯示/輸出,則需要從ISO-8859-1解碼成GBK編碼方可。
但這樣做,可能被綁定到某個類型的服務(wù)器,但也值得。實際上切換到Apache也是很簡單的。
PS : Apache服務(wù)器誒則對應(yīng)X-Sendfile頭部屬性,因很長時間不再使用Apache,這里不再測試。
源碼下載
源碼下載
posted on 2012-01-19 10:21 nieyong 閱讀(2063) 評論(0) 編輯 收藏 所屬分類: Servlet3 、Java