首先先來說下關于文件下載的原理:
服務端為客戶端提供了一個下載服務,所以服務端需要一個輸出流(把客戶請求下載的文件輸出),相對于服務端來說,客戶端需要下載接收一個文件,所以它需要一個輸入流(接收文件)。
服務器讀取要下載文件的內容,用一個Response響應流寫回并設置HTTP頭信息ContentType(文件類型)、ContentDisposition(以什么方式打開)
下面給出一個小Demo,具體代碼具體分析吧
1、首先是提供下載的頁面:download.jsp
給出超鏈接到Action并用Get方式傳遞一個文件名進行屬性注入
1 <%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%> 2 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> 3 <html> 4 <head> 5 <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> 6 <title>下載文件示例</title> 7 </head> 8 <body> 9 <h2>下載文件</h2>10 <a href="download.action?filename=照片文件.jpg">點擊下載照片文件</a>11 <a href="download.action?filename=admin.rar">點擊下載壓縮包文件</a>12 <a href="download.action?filename=總結.txt">點擊下載文本文件</a>13 </body>14 </html>
2、處理下載文件的Action:DowloadAction.java
這里提供了一個成員變量屬性來接收頁面傳遞過來的文件名,由于文件名是以Get方式傳遞過來的,中文會出現亂碼問題,所以在setter方法里需要做一些處理,也就是重新編碼。
1 package com.lcw.struts2.dowload; 2 3 import java.io.File; 4 import java.io.FileInputStream; 5 import java.io.FileNotFoundException; 6 import java.io.IOException; 7 import java.io.InputStream; 8 import java.io.UnsupportedEncodingException; 9 import java.net.URLEncoder;10 11 import org.apache.struts2.ServletActionContext;12 13 import sun.misc.BASE64Encoder;14 15 import com.opensymphony.xwork2.ActionSupport;16 /**17 * 對于客戶端來說它需要下載接收一個文件,也就是說它需要一個輸入流18 * 對于服務端來說它需要對外提供一個下載的服務,也就是說它需要一個輸出流19 */20 21 public class DowloadAction extends ActionSupport {22 23 PRivate String filename;//下載頁面傳遞了該參數,需提供setter方法接收24 25 public void setFilename(String filename) {26 //由于是get方式傳遞的,中文會出現亂碼,不能直接獲取,需轉碼27 try {28 this.filename = new String(filename.getBytes("ISO-8859-1"),"utf-8");29 } catch (UnsupportedEncodingException e) {30 e.printStackTrace();31 }32 }33 34 public String execute(){35 System.out.println("正在下載文件:"+filename);36 return SUCCESS;37 }38 39 //為客戶端提供輸入流40 public InputStream getInputStream() throws FileNotFoundException{41 String srcFile=ServletActionContext.getServletContext().getRealPath("/download")+"/"+filename;42 File file=new File(srcFile);//得到一個file對象43 return new FileInputStream(file);//返回一個文件輸入流44 }45 46 //根據不同的文件動態給出MIME文件類型47 public String getContentType(){48 //在Tomcat Conf里的web.xml有對應的映射文件49 return ServletActionContext.getServletContext().getMimeType(filename);50 }51 52 //返回一個文件名53 public String getFilename() throws IOException{54 String agent=ServletActionContext.getRequest().getHeader("user-agent");//根據http頭信息獲取對應的瀏覽器類型55 return encodeDownloadFilename(filename,agent);56 }57 58 59 //下載附件名亂碼問題 , IE和火狐 解決不同 IE默認是Url編碼 火狐默認是base64編碼60 public String encodeDownloadFilename(String filename, String agent)61 throws IOException {62 if (agent.contains("Firefox")) { // 火狐瀏覽器63 filename = "=?UTF-8?B?"64 + new BASE64Encoder().encode(filename.getBytes("utf-8"))65 + "?=";66 } else { // IE及其他瀏覽器67 filename = URLEncoder.encode(filename, "utf-8");68 }69 return filename;70 }71 }
看了上面的代碼,如果不清楚這塊知識點的朋友可能會有點蒙,別急,下面我來解析下這段代碼是怎么來的
首先Struts2的文件下載是通過一個結果集stream來完成的,在Struts2核心包里的struts-default.xml里我們可以找到這樣的一句話:
<result-type name="stream" class="org.apache.struts2.dispatcher.StreamResult"/>
我們來看下這個類(org.apache.struts2.dispatcher.StreamResult)里面有什么,Ctrl+Shift+T關聯下源代碼
這個類里面給出了很多參數,因為都有默認值,所以我們不需要全部到去改動它,只需要改變我們需要的地方就可以了。
這里來解釋下上面畫紅色框的參數內容:contentType,contentDisposition,inputName
contetType:是下載文件對應的MIME協議類型,比如:text/html,text/plain等,這個參數我們不能寫死,因為我們的下載文件的類型有很多,有時是圖片,有時是文檔等。
contentDisposition:是下載文件的打開方式,這里默認是inline也就是內聯在瀏覽器打開,如果不想關聯瀏覽器我們可以把它設置成attment以附件的形式打開。
inputNmae:這是定義一個返回流(客戶端需要的輸入流)的名稱,屬性值為inputStream。
所以我們需要在Action里面提供這些東西,利用JAVA的反射機制讓Struts2的配置文件(壓入值棧,并給出getter方法)讀取到就行了。
這里我們的下載附件名依舊會亂碼,因為IE等瀏覽器默認的編碼是URL而火狐瀏覽器默認的編碼是BASE64,我們需要在這里判斷客戶端使用的是什么瀏覽器,這個很簡單,只需要得到客戶端的HTTP頭信息Agment就行了,具體代碼在上面encodeDownloadFilename方法里以給出,拿來用便是了。
3、再來看下配置文件struts.xml的配置:
由于Action里已給出我們所需參數的getter方法,我們在這邊這需要用Ognl表達式取出,就可以根據我們要下載的文件,動態給出所需參數了,沒有設置的參數就意味著保持默認值。
1 <?xml version="1.0" encoding="UTF-8"?> 2 <!DOCTYPE struts PUBLIC 3 "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" 4 "http://struts.apache.org/dtds/struts-2.0.dtd"> 5 <struts> 6 <constant name="struts.devMode" value="true" /> 7 8 <!-- 全局國際化配置 --> 9 <constant name="struts.custom.i18n.resources" value="messages"></constant>10 11 <package name="struts2test" extends="struts-default">12 <action name="download" class="com.lcw.struts2.dowload.DowloadAction">13 <result type="stream">14 <!-- 一個流二個頭 -->15 <!-- ognl表達式,動態給出不同下載文件相相對應的MIME協議規定的類型 比如:text/html-->16 <!-- 在Action里給出getContentType壓棧 -->17 <param name="contentType">${contentType}</param>18 <!-- 下載文件打開方式 inline瀏覽器內部打開, attachment 以附件形式打開 -->19 <!-- 在Action里動態返回文件名 getFilename -->20 <param name="contentDisposition">attachment;filename=${filename}</param>21 </result>22 </action>23 </package>24 25 </struts>
然后我們新建一個文件夾download,把要下載的文件和web頁面提供的文件名一樣放入
到這里就大功告成了,看下頁面效果吧:
新聞熱點
疑難解答