1. 程式人生 > >檔案上傳與下載

檔案上傳與下載

1.檔案上傳
問題:什麼是檔案上傳?為什麼使用檔案上傳?
就是將客戶端資源,通過網路傳遞到伺服器端。
就是因為資料比較大,我們必須通過檔案上傳才可以完成將資料儲存到伺服器端操作.
檔案上傳的本質:就是IO流的操作。
演示:檔案 上傳應該 怎樣操作?
瀏覽器端:
1.method=post 只有post才可以攜帶大資料
2.必須使用<input type='file' name='f'>要有name屬性 否則瀏覽器將不會發送上傳檔案的資料。

3.encType="multipart/form-data" 必須把form的enctype屬值設為multipart/form-data.設定該值後,瀏覽器在上傳檔案時,將把檔案資料附帶在http請求訊息體中,並使用MIME協議對上傳的檔案進行描述,以方便接收方對上傳資料進行解析和處理。

<form action="${pageContext.request.contextPath}/upload1" method="post" encType="multipart/form-data">
<input type="text" name="content"><br>
<input type="file" name="f"><br>
<input type="submit" value="上傳">
</form>

伺服器端:

  如何在Servlet中讀取檔案上傳資料,並儲存到本地硬碟中?

request物件是用於獲取請求資訊。

它有一個方法  getInputStream(); 可以獲取一個位元組輸入流,通過這個流,可以讀取到

所有的請求正文資訊.

•    Request物件提供了一個getInputStream方法,通過這個方法可以讀取到客戶端提交過來的資料。但由於使用者可能會同時上傳多個檔案,在servlet端程式設計直接讀取上傳資料,並分別解析出相應的檔案資料是一項非常麻煩的工作,示例。

為方便使用者處理檔案上傳資料,Apache 開源組織提供了一個用來處理表單檔案上傳的一個開源元件( Commons-fileupload ),該元件效能優異,並且其API使用極其簡單,可以讓開發人員輕鬆實現web檔案上傳功能,因此在web開發中實現檔案上傳功能,通常使用Commons-fileupload元件實現。 檔案上傳原理:

瀏覽器端注意上述三件事,在伺服器端通過流將資料讀取到,在對資料進行解析.
將上傳檔案內容得到,儲存在伺服器端,就完成了檔案上傳。
-------------------------------------------------------------------------------------------------

使用Commons-fileupload元件實現檔案上傳,需要匯入該元件相應的支撐jar包:Commons-fileupload和commons-io。commons-io 不屬於檔案上傳元件的開發jar檔案,但Commons-fileupload 元件從1.1 版本開始,它工作時需要commons-io包的支援。

使用commons-fileupload

1.匯入jar包
commons-fileupload-1.2.1.jar  檔案上傳
commons-io-1.4.jar 它是提供的io工具.
介紹commons-fileupload
它有三個核心

1.DiskFileItemFactory類 建立DiskFileItemFactory物件,設定緩衝區大小和臨時檔案目錄

2.ServletFileUpload類 使用DiskFileItemFactory 物件建立ServletFileUpload物件,並設定上傳檔案的大小限制。

3.FileItem 

fileupload元件工作流程

2.實現步驟:
1.建立upload2.jsp頁面
<form action="${pageContext.request.contextPath}/upload2" method="post" encType="multipart/form-data">
<input type="file" name="f"><br>
<input type="submit" value="上傳">
</form>
2.建立Upload2Servlet
1.建立一個DiskFileItemFactory
DiskFileItemFactory factory=new DiskFileItemFactory();
2.建立ServletFileUpload類

ServletFileUpload upload=new ServletFileUpload(factory);

                                          //使用DiskFileItemFactory 物件建立ServletFileUpload物件

3.解析所有上傳資料
List<FileItem> items = upload.parseRequest(request);

呼叫ServletFileUpload.parseRequest方法解析request物件,得到一個儲存了所有上傳內容的List物件。

4.遍歷items集合,集合中的每一項,就是一個上傳資料.

對list進行迭代,每迭代一個FileItem物件,呼叫其isFormField方法判斷是否是上傳檔案

•    True 為普通表單欄位,則呼叫getFieldName、getString方法得到欄位名和欄位值

•    False 為上傳檔案,則呼叫getInputStream方法得到資料輸入流,從而讀取上傳資料。

1.isFormField();
2.getFieldName();
返回值String,得到元件名稱  <input name="">
3.getString();
這個方法可以獲取非上傳元件的內容,相當於  getParameter方法作用。
如果是上傳元件,上傳的檔案是文字檔案,可以獲取到文字檔案的內容。

但是如果不是文字檔案,例如:是一張圖片,這樣獲取不合適

                                4.getName();
返回值是String,得到的是上傳檔案的名稱.
注意:瀏覽器不同,它們得到的效果不一樣。
1.包含全路徑名稱  例如: C:\Users\Administrator\Desktop\a.txt
2.只包含上傳檔名稱 例如:a.txt

5.獲取上傳檔案的內容,儲存到伺服器端.
item.getInputStream();它是用於讀取上傳檔案內容的輸入流.
使用檔案複製操作就可以完成檔案上傳。

IOUtils.copy(item.getInputStream(), fos);

----------------------------------------------------------------------------------------
核心API介紹
1.DiskFileItemFactory
作用:可以設定快取大小以及臨時檔案儲存位置.
預設快取大小是  10240(10k).
臨時檔案預設儲存在系統的臨時檔案目錄下.(可以在環境變數中檢視)
1.new DiskFileItemFactory();
快取大小與臨時檔案儲存位置使用預設的.
2.DiskFileItemFactory(int sizeThreshold, File repository) 
sizeThreshold :快取大小
repository:臨時檔案儲存位置
注意,對於無引數構造,也可以設定快取大小以及臨時檔案儲存位置.
 setSizeThreshold(int sizeThreshold)

 setRepository(File repository)

public void setSizeThreshold(intsizeThreshold) 設定記憶體緩衝區大小,預設10K

public void setRepository(java.io.Filerepository)設定臨時檔案存放位置,預設System.getProperty("java.io.tmpdir").

記憶體緩衝區: 上傳檔案時,上傳檔案的內容優先儲存在記憶體緩衝區中,當上傳檔案大小超過緩衝區大小,就會在伺服器端產生臨時檔案

臨時檔案存放位置: 儲存超過了記憶體緩衝區大小上傳檔案而產生臨時檔案

* 產生臨時檔案可以通過 FileItem的delete方法刪除

                DiskFileItemFactory factory = new DiskFileItemFactory(); //使用預設的.
File file = new File(this.getServletContext().getRealPath("/temp"));// 獲取temp目錄部署到tomcat後的絕對磁碟路徑
DiskFileItemFactory factory = new DiskFileItemFactory(1024 * 100, file);

2.ServletFileUpload
1.ServletFileUpload upload=new ServletFileUpload(factory);
 建立一個上傳工具,指定使用快取區與臨時檔案儲存位置.
2.List<FileItem> items=upload.parseRequest(request);
它是用於解析request物件,得到所有上傳項.每一個FileItem就相當於一個上傳項.
3.boolean flag=upload.isMultipartContent(request);
用於判斷是否是上傳.

可以簡單理解,就是判斷encType="multipart/form-data";

static boolean isMultipartContent(javax.servlet.http.HttpServletRequest request)  判斷request的編碼方式是否為multipart/form-data

4.設定上傳檔案大小
void setFileSizeMax(long fileSizeMax) 設定單個檔案上傳大小 
void  setSizeMax(long sizeMax) 設定總檔案上傳大小 
5.解決上傳檔案中文名稱亂碼
setHeaderEncoding("utf-8");

注意:如果使用reqeust.setCharacterEncoding("utf-8")也可以,但不建議使用。

3.FileItem
1.isFormField
用於判斷是否是上傳元件.
如果是<input type="file">返回的就是false,否則返回true.
2.getFieldName();
返回值String,得到元件名稱  <input name="">
3.getName();
返回值是String,得到的是上傳檔案的名稱.
注意:瀏覽器不同,它們得到的效果不一樣。
1.包含全路徑名稱  例如: C:\Users\Administrator\Desktop\a.txt
2.只包含上傳檔名稱 例如:a.txt
4.getString();
這個方法可以獲取非上傳元件的內容,相當於  getParameter方法作用。
問題:如果資訊是中文,會出現亂碼,解決方案  getString("utf-8");
5.獲取上傳檔案的內容,儲存到伺服器端.
item.getInputStream();它是用於讀取上傳檔案內容的輸入流.
使用檔案複製操作就可以完成檔案上傳。
6.刪除臨時檔案
item.delete();
------------------------------------------------------------
總結:關於檔案上傳時的亂碼問題:
1.上傳檔名稱亂碼
ServletFileUpload.setHeaderEncoding("utf-8");
2.非上傳元件內容亂碼
FileItem.getString("utf-8");
3.思考:上傳檔案資訊是否會亂碼,需要解決嗎?

不需要解決,因為我們在上傳時,使用的位元組流來進行復制。

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.io.IOUtils;
//commons-fileupload api詳解
@SuppressWarnings("all")
public class Upload3Servlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;charset=utf-8");
// 1.建立 DiskFileItemFactory
// DiskFileItemFactory factory = new DiskFileItemFactory(); //使用預設的.
File file = new File(this.getServletContext().getRealPath("/temp"));// 獲取temp目錄部署到tomcat後的絕對磁碟路徑
DiskFileItemFactory factory = new DiskFileItemFactory(1024 * 100, file); // 使用預設的.
// 2.建立ServletFileUpload
ServletFileUpload upload = new ServletFileUpload(factory);
boolean flag = upload.isMultipartContent(request); // 用於判斷是否是上傳操作.
if (flag) {
// 解決上傳檔名稱中文亂碼
upload.setHeaderEncoding("utf-8");
// 設定上傳檔案大小
//upload.setSizeMax(1024 * 1024 * 10);// 總大小為10m
try {
List<FileItem> items = upload.parseRequest(request);// 解決request,得到所有的上傳項FileItem
// 3.得到所有上傳項
for (FileItem item : items) {
if (item.isFormField()) {
// 非上傳元件
System.out.println("元件名稱:" + item.getFieldName());
System.out.println("內容:" + item.getString("utf-8")); // 解決亂碼問題
} else {
// 上傳元件
System.out.println("元件名稱:" + item.getFieldName());
System.out.println("上傳檔名稱:" + item.getName());
String name = item.getName(); // 上傳檔名稱
System.out.println(name);
name = name.substring(name.lastIndexOf("\\") + 1);
IOUtils.copy(item.getInputStream(),
new FileOutputStream("d:/upload/" + name));
// 刪除臨時檔案
item.delete();
}
}
} catch (FileUploadException e) {
// e.printStackTrace();
response.getWriter().write(e.getMessage());
return;
}
} else {
response.getWriter().write("不是上傳操作");
return;
}
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}

=====================================================================================================

多檔案上傳

每次動態增加一個檔案上傳輸入框,都把它和刪除按紐放置在一個單獨的div中,並對刪除按紐的onclick事件進行響應,使之刪除刪除按紐所在的div。

 <script type="text/javascript">   
function addFile(){
var div=document.getElementById("content");
div.innerHTML+="<div><input type='file' name='f'><input type='button' value='remove file' onclick='removeFile(this)'></div>";
}
function removeFile(btn){
document.getElementById("content").removeChild(btn.parentNode)
}
</script>
伺服器端程式碼不需要改變.
------------------------------------------------------------------------
關於檔案上傳的注意事項:
1.上傳檔案在伺服器端儲存位置問題
1.儲存在可以被瀏覽器直接訪問的位置
例如:商城的商品圖片
儲存在工程的WebRoot下的路徑(不包含META-INF以及WEB-INF目錄及其子目錄)
2.儲存在不能被瀏覽器直接訪問的位置
例如:付費的視訊。
1.工程中   META-INF  WEB-INF目錄及其子目錄
2.不在工程中的伺服器的磁碟目錄下.
------------------------------------------------
2.上傳檔案在同一個目錄重名問題 
在開發中解決這個問題,可以給上傳檔案起隨機名稱。
1.使用毫秒值

2.使用uuid,UUID的標準型式包含32個16進位制數字,以連字號分為五段,形式為8-4-4-4-12的32個字元

UUID 的目的是讓分散式系統中的所有元素,都能有唯一的辨識資訊,而不需要透過中央控制端來做辨識資訊的指定。如此一來,每個人都可以建立不與其它人衝突的 UUID。在這樣的情況下,就不需考慮資料庫建立時的名稱重複問題。目前最廣泛應用的 UUID,即是微軟的 Microsoft's Globally Unique Identifiers (GUIDs),而其他重要的應用,則有 Linux ext2/ext3 檔案系統、LUKS 加密分割區、GNOME、KDE、Mac OS X 等等。

---------------------------------------------------
3.同一目錄下檔案過多
只需要分目錄就可以.
1) 按照上傳時間進行目錄分離 (周、月 )
2) 按照上傳使用者進行目錄分離 ----- 為每個使用者建立單獨目錄 
3) 按照固定數量進行目錄分離 ------ 假設每個目錄只能存放3000個檔案 ,每當一個目錄存滿3000個檔案後,建立一個新的目錄
4)按照檔名的hashcode進行目錄分離.
public static String generateRandomDir(String uuidFileName) {
// 獲得唯一檔名的hashcode
int hashcode = uuidFileName.hashCode();
// 獲得一級目錄
int d1 = hashcode & 0xf;       
// 獲得二級目錄
int d2 = (hashcode >>> 4) & 0xf;
return "/" + d2 + "/" + d1;// 共有256目錄l

}

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
        <title>多檔案上傳</title>    
    <script type="text/javascript">    
    function addFile(){
    var div=document.getElementById("content");    
    div.innerHTML+="<div><input type='file' name='f'><input type='button' value='remove file' onclick='removeFile(this)'></div>";
    }    
    function removeFile(btn){    
    document.getElementById("content").removeChild(btn.parentNode);    
    }
    </script>    
  </head>  
  <body>
<input type="button" value="add File" onclick="addFile();">
<br>
<br>
<form action="${pageContext.request.contextPath}/upload4" method="post" encType="multipart/form-data">
<input type="file" name="f"><br>
<div id="content">
</div>
<input type="submit" value="上傳">
</form>
  </body>

</html>

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.io.IOUtils;
import cn.itcast.utils.FileUploadUtils;
public class Upload4Servlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;charset=utf-8");
// 1.建立 DiskFileItemFactory
File file = new File(this.getServletContext().getRealPath("/temp"));// 獲取temp目錄部署到tomcat後的絕對磁碟路徑
DiskFileItemFactory factory = new DiskFileItemFactory(1024 * 100, file); // 使用預設的.
// 2.建立ServletFileUpload
ServletFileUpload upload = new ServletFileUpload(factory);
boolean flag = upload.isMultipartContent(request); // 用於判斷是否是上傳操作.
if (flag) {
// 解決上傳檔名稱中文亂碼
upload.setHeaderEncoding("utf-8");
// 設定上傳檔案大小
// upload.setSizeMax(1024 * 1024 * 10);// 總大小為10m
try {
List<FileItem> items = upload.parseRequest(request);// 解決request,得到所有的上傳項FileItem
// 3.得到所有上傳項
for (FileItem item : items) {
if (!item.isFormField()) {
// 上傳元件
String name = item.getName(); // 上傳檔名稱
// 得到上傳檔案真實名稱
String filename = FileUploadUtils.getRealName(name);
// 得到隨機名稱
String uuidname = FileUploadUtils
.getUUIDFileName(filename);
// 得到隨機目錄
String randomDirectory = FileUploadUtils
.getRandomDirectory(filename);
// 注意:隨機目錄可能不存在,需要建立.
String parentPath = this.getServletContext()
.getRealPath("/upload");
File rd = new File(parentPath, randomDirectory);
if (!rd.exists()) {
rd.mkdirs();
}
IOUtils.copy(item.getInputStream(),
new FileOutputStream(new File(rd, uuidname)));

// 刪除臨時檔案
item.delete();
}
}
} catch (FileUploadException e) {
// e.printStackTrace();
response.getWriter().write(e.getMessage());
return;
}
} else {
response.getWriter().write("不是上傳操作");
return;
}
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}

}

import java.io.File;
import java.util.UUID;
public class FileUploadUtils {
// 得到上傳檔案真實名稱 c:\a.txt a.txt
public static String getRealName(String filename) {
int index = filename.lastIndexOf("\\") + 1;
return filename.substring(index);
}
// 獲取隨機名稱 a.txt
public static String getUUIDFileName(String filename) {
int index = filename.lastIndexOf(".");
if (index != -1) {
return UUID.randomUUID() + filename.substring(index);
} else {
return UUID.randomUUID().toString();
}
}
// 目錄分離演算法
public static String getRandomDirectory(String filename) {
int hashcode = filename.hashCode();
System.out.println(Integer.toBinaryString(hashcode));
int a = hashcode & 0xf;
hashcode = hashcode >>> 4;
int b = hashcode & 0xf;
return "/" + a + "/" + b;
}
}

========================================================================================
檔案下載
檔案下載的方式:
1.超連線下載
2.伺服器端通過流下載(伺服器端程式設計)
1.超連線下載
download1.jsp
<a href='${pageContext.request.contextPath}/upload/a.bmp'>a.bmp</a><br>
<a href='${pageContext.request.contextPath}/upload/a.doc'>a.doc</a><br>
<a href='${pageContext.request.contextPath}/upload/a.txt'>a.txt</a><br>
<a href='${pageContext.request.contextPath}/upload/tk.mp3'>tk.mp3</a><br>
注意:如果檔案可以直接被瀏覽器解析,那麼會在瀏覽器中直接開啟,不能被瀏覽器直接解析,就是下載操作。
             直接開啟的要想下載 ,右鍵另存為。
超連線下載,要求下載 的資源,必須是可以直接被瀏覽器直接訪問的。
客戶端訪問伺服器靜態資原始檔時,靜態資原始檔是通過 預設Servlet返回的,

在tomcat配置檔案conf/web.xml 找到 --- org.apache.catalina.servlets.DefaultServlet

2.在伺服器端程式設計完成下載.

編寫伺服器程式,讀取伺服器端檔案,完成下載,必須設定兩個頭資訊 ,來自MIME協議 Content-Type  Content-Disposition 

1.建立download2.jsp
<a href='${pageContext.request.contextPath}/download?filename=a.bmp'>a.bmp</a><br>
<a href='${pageContext.request.contextPath}/download?filename=a.doc'>a.doc</a><br>
<a href='${pageContext.request.contextPath}/download?filename=a.txt'>a.txt</a><br>
<a href='${pageContext.request.contextPath}/download?filename=tk.mp3'>tk.mp3</a><br>
2.建立DownloadServlet
// 1.得到要下載 的檔名稱
String filename = request.getParameter("filename");
//2.判斷檔案是否存在
File file = new File("d:/upload/" + filename);
if (file.exists())
//3.進行下載 
原理:就是通過response獲取一個輸出流,將要下載的檔案內容寫回到瀏覽器端就可以了.
注意:要想通過程式設計的方式,實現檔案下載,
1.要設定mimetype型別
resposne.setContextType(String mimeType);
問題:怎樣可以得到要下載檔案的mimeType型別?
ServletContext.getMimeType(String filename);

如果設定了mimeType,瀏覽器能解析的就直接展示了,不能解析的,直接下載.

2.設定一個響應頭,設定後的效果,就是無論返回的是否可以被瀏覽器解析,就是下載 。
response.setHeader("content-disposition","attachment;filename=下載檔名稱");

MIME(Multipurpose Internet Mail Extensions)多用途網際網路郵件擴充套件型別。是設定某種副檔名的檔案用一種應用程式來開啟的方式型別,當該副檔名檔案被訪問的時候,瀏覽器會自動使用指定應用程式來開啟。多用於指定一些客戶端自定義的檔名,以及一些媒體檔案開啟方式。

頭欄位
MIME頭根據在郵件包中的位置,大體上分為MIME資訊頭和MIME段頭。(譯者:MIME資訊頭指整個郵件的頭,而MIME段頭只每個MIME段的頭。)
MIME資訊頭有:
MIME-Version:
這個頭提供了所用MIME的版本號。這個值習慣上為1.0。
Content-Type:
它定義了資料的型別,以便資料能被適當的處理。有效的型別有:text,image,audio,video,applications,multipart和message。注意任何一個二進位制附件都應該被叫做application/octet- stream。這個頭的一些用例為:image/jpg, application/mswork,multipart/mixed,這只是很少的一部分。
Content-Transfer-Encoding:
這是所有頭中最重要的一個,因為它說明了對資料所執行的編碼方式,客戶/MUA 將用它對附件進行解碼。對於每個附件,可以使用7bit,8bit,binary ,quoted-printable,base64和custom中的一種編碼方式。7bit編碼是用在US ASCII字符集上的常用的一種編碼方式,也就是,保持它的原樣。8bit和binary編碼一般不用。對人類可讀的標準文字,如果傳輸要經過對格式有影響的閘道器時對其進行保護,可以使用quoted printable 。Base64是一種通用方法,在需要決定使用哪一種編碼方法時,它提供了一個不用費腦子的選擇;它通常用在二進位制,非文字資料上。注意,任何非7bit 資料必須用一種模式編碼,這樣它就可以通過Internet郵件閘道器!
Content-ID:
如果Content-Type是message/external-body或multipart/alternative時,這個頭就有用了,它超出了本文的範圍。
Content-Description:
這是一個可選的頭。它是任何資訊段內容的自由文字描述。描述必須使用us-ascii碼。
Content-Disposition:
一個試驗性的頭,它用於給客戶程式/MUA提供提示,來決定是否在行內顯示附件或作為單獨的附件。
MIME段頭(出現在實際的MIME附件部分的頭),除了MIME-Version頭,可以擁有以上任何頭欄位。如果一個MIME頭是資訊塊的一部分,它將作用於整個資訊體。例如,如果Content-Transfer-Encoding顯示在資訊(指整個資訊)頭中,它應用於整個資訊體,但是如果它顯示在一個MIME段裡,它"只能"用於那個段中。
注意:其可以對自動對收到的郵件進行解密。

總結:伺服器端程式設計下載:

1.將下載的檔案通過resposne.getOutputStream()流寫回到瀏覽器端。
2.設定mimeType  response.setContentType(getServletContext.getMimeType(String filename));// conf/web.xml查詢
3.設定響應頭,目的是永遠是下載操作
response.setHeader("content-disposition","attachment;filename=下載檔名稱");
--------------------------------------
檔案下載時的亂碼問題:
1.關於下載時中文名稱資源查詢不到
原因:<a href='${pageContext.request.contextPath}/download?filename=天空.mp3'>空.mp3</a>
  這是get請求。  
  在伺服器端:
  String filename = request.getParameter("filename");  
 解決: new String(filename.getBytes("iso8859-1"),"utf-8");  
2.下載檔案顯示時的中文亂碼問題,處理IE瀏覽器與Firefox瀏覽器亂碼問題
response.setHeader("content-disposition", "attachment;filename="+filename);
IE:要求filename必須是utf-8
firefox:要求filename必須是base64編碼.
問題:怎樣判斷瀏覽器?
String agent=request.getHeader("user-agent");
                                                if (agent.contains("MSIE")) {
// IE瀏覽器
filename = URLEncoder.encode(filename, "utf-8");
filename = filename.replace("+", " ");
} else if (agent.contains("Firefox")) {
// 火狐瀏覽器
BASE64Encoder base64Encoder = new BASE64Encoder();
filename = "=?utf-8?B?"
+ base64Encoder.encode(filename.getBytes("utf-8"))
+ "?=";
} else if (agent.contains("Chrome")) {
// google瀏覽器
filename = URLEncoder.encode(filename, "utf-8");
} else {
// 其它瀏覽器
filename = URLEncoder.encode(filename, "utf-8");

}

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>   
    <title>My JSP 'index.jsp' starting page</title>
  </head>  
  <body>
<a href='${pageContext.request.contextPath}/download?filename=a.bmp'>a.bmp</a><br>
<a href='${pageContext.request.contextPath}/download?filename=a.doc'>a.doc</a><br>
<a href='${pageContext.request.contextPath}/download?filename=a.txt'>a.txt</a><br>
<a href='${pageContext.request.contextPath}/download?filename=天空.mp3'>天空.mp3</a><br>
  </body>

</html>

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URLEncoder;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import sun.misc.BASE64Encoder;
public class DownloadServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// 1.得到要下載 的檔名稱
String filename = request.getParameter("filename");
filename = new String(filename.getBytes("iso8859-1"), "utf-8");// 解決中文亂碼
// 2.在d:/upload目錄下查詢這個檔案是否存在
File file = new File("d:/upload/" + filename);
if (file.exists()) {
// /檔案存在,完成下載
// 下載注意事項1--設定下載檔案的mimeType
String mimeType = this.getServletContext().getMimeType(filename);
response.setContentType(mimeType);
String agent = request.getHeader("user-agent");
if (agent.contains("MSIE")) {
// IE瀏覽器
filename = URLEncoder.encode(filename, "utf-8");
} else if (agent.contains("Firefox")) {
// 火狐瀏覽器
BASE64Encoder base64Encoder = new BASE64Encoder();
filename = "=?utf-8?B?"
+ base64Encoder.encode(filename.getBytes("utf-8"))
+ "?=";
} else {
// 其它瀏覽器
filename = URLEncoder.encode(filename, "utf-8");
}
// 下載注意事項2--永遠是下載
response.setHeader("content-disposition", "attachment;filename="
+ filename);
FileInputStream fis = new FileInputStream(file); // 讀取要下載檔案的內容
OutputStream os = response.getOutputStream(); // 將要下載的檔案內容通過輸出流寫回到瀏覽器端.
int len = -1;
byte[] b = new byte[1024 * 100];
while ((len = fis.read(b)) != -1) {
os.write(b, 0, len);
os.flush();
}
os.close();
fis.close();
} else {
throw new RuntimeException("下載資源不存在.");
}
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}

}

<%@page import="java.io.File"%>
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>使用遞迴來完成下載upload目錄下所有檔案</title>
</head>
<body>
<!-- 使用遞迴操作 -->
<%!//宣告一個方法
public void getFile(File file) {
if (file.isDirectory()) {
//是目錄
File[] fs = file.listFiles();
for (int i = 0; i < fs.length; i++) {
getFile(fs[i]); //遞迴呼叫
}
} else if (file.isFile()) {
//是檔案
System.out.println(file.getName());
}
}%>
<%
String path = "D:\\java1110\\workspace\\day22_2\\WebRoot\\upload";
File uploadDirectory = new File(path);
getFile(uploadDirectory);
%>
</body>

</html>

<%@page import="java.io.File"%>
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>使用佇列來完成下載upload目錄下所有檔案</title>
</head>
<body>
<!-- 使用佇列操作 -->
<%
String path = "D:\\java1110\\workspace\\day22_2\\WebRoot\\upload";
File uploadDirectory = new File(path);
//建立一個佇列
Queue<File> queue = new LinkedList<File>();
queue.offer(uploadDirectory);
while (!queue.isEmpty()) { //如果佇列不為空
File f = queue.poll(); //從佇列中獲取一個File
if(f.isDirectory()){//是目錄,將目錄下所有檔案遍歷出來,儲存到佇列中
File[] fs = f.listFiles();
for (int i = 0; i < fs.length; i++) {
queue.offer(fs[i]);
}
}else{
String absolutePath=(f.getAbsolutePath());
String p=absolutePath.substring(absolutePath.lastIndexOf("\\upload"));
out.println("<a href='/day22_2"+p+"'>"+f.getName()+"</a><br>");
}
}
%>
</body>
</html>

============================================================================================================
擴充套件:使用佇列來優化遞迴操作.
佇列特點:先進先出.
在jdk中有一個介面Queue 它有一個實現類叫LinkedList它其實就是一個佇列。
如果要使用佇列,插入 offer  獲取使用 poll
使用佇列來優化遞迴操作:是可以解決目錄層次過多問題。
因為:遞迴操作可以理解成是縱向的遍歷,如果目錄層次比較多,在記憶體中儲存的資料也多,會引起溢位。
使用佇列,它是橫向遍歷,一層一層遍歷,可以解決目錄層次比較多問題。
因為使用佇列,最多時候在記憶體中只儲存了一層的資訊。
最常用的就是樹型結構。