<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    聶永的博客

    記錄工作/學習的點點滴滴。

    Servlet 3.0筆記之文件下載的那點事情

    使用Servlet 3.0 提供文件下載,當然了任何Servlet版本,都可以做到,這里僅僅作為知識的積累。下面貼出代碼,防止忘卻。

    一。常規方式文件下載示范


    很普通,簡單易用,不多說。一般情況下,夠用了。

    二。偽零拷貝(zero copy)方式文件下載示范

    變化不大,關鍵代碼在于:利用
    FileChannel.transferTo(long position, long count, WritableByteChannel target)
    方法達到零拷貝(zero copy)目的。把HttpServletResponse的輸出對象(ServletOutputStream)利用Channels.newChannel(OutputStream out)工具,構建一個WritableByteChannel對象而已。
    OutputStream out = response.getOutputStream();
    WritableByteChannel outChannel = Channels.newChannel(out);
    測試代碼:

    心存疑慮的是,這個是偽零拷貝方式實現。查看一下Channels.newChannel的源碼:
    public static WritableByteChannel newChannel(final OutputStream out) {
    if (out == null) {
    throw new NullPointerException();
    }

    if (out instanceof FileOutputStream &&
    FileOutputStream.class.equals(out.getClass())) {
    return ((FileOutputStream)out).getChannel();
    }

    return new WritableByteChannelImpl(out);
    }
    因為輸入的方法參數為ServletOutputStream類型實例,因此只能返回一個新構建的WritableByteChannelImpl對象。具體構建:
    private static class WritableByteChannelImpl
    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;
    }
    }
    很顯然,也是屬于內存類型的拷貝了,只能算作偽零拷貝實現了。

    三。轉發到文件服務器上

    一般常識為,讓最擅長的人來做最擅長的事情,是為高效。使用類如Nginx高效的Web服務器專門處理文件下載業務,達到零拷貝的目的,也是最佳搭配組合。Nginx可以利用header元數據X-Accel-Redirect來控制文件下載行為,甚是不錯。利用JAVA進行業務邏輯判斷,若符合規則,則提交給Nginx進行處理文件的下載,否則,返回給終端用戶權限不夠等信息。
    用于控制用戶是否具有資格進行文件下載業務的控制器:

    當然,這個僅僅用于演示,邏輯簡單。因為需要和nginx服務器進行配合,構建一個Server,其配置文件:

    我們在nginx配置文件中,設置/dowloads/目錄是不允許直接訪問的,必須經由/download/控制器進行轉發方可。經測試,中文名不會出現亂碼問題,保存的文件也是我們所請求的文件,同名,也不會出現亂碼問題。但是,若在后臺獲取文件名,用于顯示/輸出,則需要從ISO-8859-1解碼成GBK編碼方可。
    但這樣做,可能被綁定到某個類型的服務器,但也值得。實際上切換到Apache也是很簡單的。
    PS : Apache服務器誒則對應X-Sendfile頭部屬性,因很長時間不再使用Apache,這里不再測試。
    源碼下載
    參考資料:
    1. 通過零拷貝實現有效數據傳輸

    2. Most effective way to write File to ServletOutputStream
    3. Java NIO FileChannel versus FileOutputstream performance / usefulness
    4. NginxChsXSendfile

    posted on 2012-01-19 10:21 nieyong 閱讀(2057) 評論(0)  編輯  收藏 所屬分類: Servlet3Java

    公告

    所有文章皆為原創,若轉載請標明出處,謝謝~

    新浪微博,歡迎關注:

    導航

    <2012年1月>
    25262728293031
    1234567
    891011121314
    15161718192021
    22232425262728
    2930311234

    統計

    常用鏈接

    留言簿(58)

    隨筆分類(130)

    隨筆檔案(151)

    個人收藏

    最新隨筆

    搜索

    最新評論

    閱讀排行榜

    評論排行榜

    主站蜘蛛池模板: 亚洲人成人无码网www国产| 国产一区二区三区在线免费观看 | 亚洲国产日韩一区高清在线| 老司机69精品成免费视频| 激情亚洲一区国产精品| 亚洲日本一区二区一本一道| 久久午夜羞羞影院免费观看| 亚洲av成人一区二区三区观看在线 | 成人影片一区免费观看| 亚洲一本到无码av中文字幕| 国产亚洲精品线观看动态图| 国产免费女女脚奴视频网| 免费一级毛片在线播放放视频| 亚洲无线电影官网| 免费欧洲毛片A级视频无风险| 99久热只有精品视频免费看| 视频一区在线免费观看| 亚洲日韩国产精品无码av| 亚洲偷自拍拍综合网| 日韩亚洲国产高清免费视频| 中文字幕免费在线视频| 亚洲精品GV天堂无码男同| 久久精品国产亚洲AV电影 | 亚洲色成人四虎在线观看| 亚洲成AV人片在线观看无码| 国产一级做a爱免费视频| 国产成人精品免费视频动漫| 99在线热播精品免费99热| 久久水蜜桃亚洲AV无码精品| 亚洲女人18毛片水真多| 亚洲精品午夜无码电影网| 黑人粗长大战亚洲女2021国产精品成人免费视频 | 亚洲最大av资源站无码av网址| 亚洲精品国产精品乱码不99 | 4399好看日本在线电影免费| a级毛片在线免费| 色吊丝性永久免费看码| 亚洲七久久之综合七久久| 亚洲欧洲校园自拍都市| 亚洲av无码无在线观看红杏| 久久亚洲国产精品五月天婷|