1. 程式人生 > 其它 >JavaWeb20-檔案上傳;下載(Java真正的全棧開發)

JavaWeb20-檔案上傳;下載(Java真正的全棧開發)

檔案上傳&下載一.檔案上傳

1. 檔案上傳介紹

要將客戶端(瀏覽器)大資料儲存到伺服器端,不將資料直接儲存到資料庫中,而是要將資料儲存到伺服器所在的磁碟上,這就要使用檔案上傳。

作用:減少了資料庫伺服器的壓力,對資料的操作更加靈活

2. 檔案上傳原理分析

所謂的檔案上傳就是伺服器端通過request物件獲取輸入流,將瀏覽器端上傳的資料讀取出來,儲存到伺服器端

瀏覽器端操作

1.請求方式必須是post

2.使用<input type=’file’ name=’xxx’>,必須有name屬性且有值

3.表單必須設定encType=”multipart/form-data” (ajax見過enctype)

伺服器端操作

通過request物件,獲取inputStream,就可以將瀏覽器提交的所有資料讀取到.

注意:

這時候,獲取其他上傳引數的時候,通過request.getParameter(“”)獲取不到了.

3. Commons-fileupload介紹與入門案例

commons-fileupload介紹

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 API簡單介紹

DiskFileItemFactory,用於設定快取大小及臨時檔案儲存位置

ServletFileUpload,真正用於檔案上傳的核心類

FIleItem,代表的是一個上傳項

使用步驟:

1.匯入jar包(commons-fileupload-1.2.1.jar和commons-io-1.4.jar)

2.建立一個磁碟檔案項工廠

DiskFileItemFactory fatory=new DiskFileItemFactory()

3.建立檔案上傳核心物件

ServletFileUpload upload=new SerlvetFileUpload(fatory)

4.核心物件解析request

List<FileItem> list=upload.parseReqeust(request);

5.遍歷list獲取裡面的內容

for (FileItem f : list) {
if(!f.isFormField()){
InputStream is = f.getInputStream();
/*byte[] b=new byte[1024];
intlen=-1;
FileOutputStream os = new FileOutputStream(new File("f:/1.txt"));
while((len=is.read(b))!=-1){
os.write(b);
}
os.close();
is.close();
*/
FileOutputStream os = new FileOutputStream(new File("c:/1.txt"));
IOUtils.copy(is, os);
os.close();
is.close();
}
}

入門案例分析

在瀏覽器端建立一個可以上傳檔案的表單,在伺服器端通過commons-fileupload完成檔案上傳。

瀏覽器端注意三件事情:

表單的提交方式為post

在表單上新增屬性 encType=”multipart/form-data”

使用<input type=’file’ >,新增name屬性且有值

伺服器端:

建立DiskFileItemFactory

建立ServletFileUpload

通過ServletFileUpload的parseRequest方法得到所有的FileItem

入門案例實現

瀏覽器端

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

伺服器端

// 1.建立一個 DiskFileItemFactory

DiskFileItemFactory factory = new DiskFileItemFactory();

// 2.建立一個ServletFileUpload

ServletFileUpload upload = new ServletFileUpload(factory);

// 3.完成上傳操作

try {

// 3.1 得到所有上傳項

List<FileItem> items = upload.parseRequest(request);

// 3.2遍歷上傳項

for (FileItem item : items) {

// 3.3判斷是否是上傳元件

if (!item.isFormField()) {

// 3.4 將檔案真正上傳

IOUtils.copy(item.getInputStream(), new FileOutputStream(
"f:/upload/a.txt"));
}
}
} catch (FileUploadException e) {
e.printStackTrace();
}

4. Commons-fileupload詳解-DiskFileItemFactory

:磁碟檔案項工廠

常用方法:

new DiskFileItemFactory();構造方法

new DiskFileItemFactory(sizeThreshold, repository);構造方法

設定快取大小

factory.setSizeThreshold(1024*1024); //設定為1m 預設是10k 單位為byte

設定臨時檔案儲存位置

File temp=new File(this.getServletContext().getRealPath("/temp"));

factory.setRepository(file); //可以指定臨時檔案儲存位置,預設是系統的臨時檔案儲存位置(設定緩衝區位置)

5. Commons-fileupload詳解-ServletFileUpload

:檔案上傳核心物件

new ServletFileUpload(factory);

parseRequest方法

List<FileItem> pareRequest(HttpServletRequest request)

得到所有的上傳資訊,將每一部分對映成FileItem物件.

isMultipartContent方法

boolean isMultipartContent(HttpServletRequest request)

這個方法返回值是boolean,它是用於判斷當前表單是否是一個上傳的表單,

簡單說,就判斷它的encType的值是否是 multipart/form-data.

若使用了enctype=multipart/form-data,在後臺就不可以使用request.getParameter(name)

upload.setHeaderEncoding(“utf-8”)方法 設定檔名稱的編碼

用於解決上傳檔名稱中文亂碼問題

設定上傳檔案大小

void setFileSizeMax(long fileSizeMax) 設定單個檔案上傳大小

void setSizeMax(long sizeMax) 設定總檔案上傳大小

6. Commons-fileupload詳解-FileItem

:檔案項

isFormField方法

這個方法返回的是boolean型別,

它是判斷當前元件是否是上傳元件 簡單說,就是判斷type="file",

如果返回true,代表不是上傳元件,返回false,代表是上傳元件

getName方法

獲取上傳元件的上傳檔案的名稱,如果是非上傳元件,返回的是null

getFieldName方法

獲取元件名稱,簡單說,就是表單的元素的name值。(獲取前臺頁面的name屬性)

getString方法

獲取非上傳元件的value值,(獲取的是表單填寫的內容)

通過它也可以獲取上傳的檔案內容,但是,使用它獲取不合適。

如果使用了commons-fileupload進行檔案上傳,而上傳表單中包含了非上傳元件,獲取 其值,不能使用request獲取.

getString()有一個過載的方法 getString(String encoding)

getString(utf-8)可以解決亂 碼問題

getInputStream方法

通過FileItem.getInputStream();可以獲取一個輸入流,這個輸入流就可以

讀取出上傳檔案內容。

InputStream is = item.getInputStream(); // 用於讀取上傳檔案內容的輸入流.

FileOutputStream fos = new FileOutputStream("f:/upload/" + filename);
int len = -1;
byte[] b = new byte[1024 * 10];
while ((len = is.read(b)) != -1) {
fos.write(b, 0, len);
fos.flush();
}
fos.close();
is.close();

可以使用IOUtils工具完成檔案copy操作

IOUtils.copy(is, fos);

delete()方法

它是用於上傳完成後,刪除臨時檔案的

7. 多檔案上傳

我們在寫郵件中可以新增多個附件,那麼我們在檔案上傳時,是不是也可以上傳多個檔案哪,答案是一定的,那麼怎樣實現多個檔案上傳哪?

我們可以通過js實現瀏覽器端的上傳檔案框的動態新增。伺服器端程式碼不需要改變.

function addFile() {

//1.得到id叫content的div

var div=document.getElementById("content");

//2.向其中新增一段html程式碼

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);
}
///////////////////////////////////////////////////////////////////////////
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<script type="text/javascript">
//新增一個上傳框
functionaddFile(){
//獲取新增位置
varobj=document.getElementById("fileDiv");
//新增檔案筐
obj.innerHTML+=("<div><input type='file' name='file'><input type='button' value='刪除' onclick='delFile(this)'/></div>");
}
//刪除當前檔案上傳框
functiondelFile(obj){
document.getElementById("fileDiv").removeChild(obj.parentNode);
}
</script>
<body>
<input type='button' value='新增檔案' onclick="addFile()">
<form action="${pageContext.request.contextPath }/upload4" method='post' enctype="multipart/form-data">
<input type="file" name="file"><br>
<div id='fileDiv'></div>
<input type='submit' >
</form>
</body>
</html>

8. 檔案上傳問題-檔案重名

每一個客戶端都可以進行檔案上傳操作,那麼當我們上傳的檔案過多,一定會出現同名的檔案,那麼在伺服器端只能儲存一個,對於這個問題,我們在上傳檔案時,就需要考慮檔案重名問題.

一般情況下,對於上傳檔案,為了保證不重名,會給檔案起一個隨機名.

一種方案是使用uuid.

一種方案是使用毫秒值

9. 檔案上傳問題-儲存位置

本質就是上傳的檔案是否允許瀏覽器端直接訪問。

例如:商品新增時需要一個圖片,這個圖片一定是可以直接被瀏覽器端訪問的。

1.允許被瀏覽器端訪問:放置在WebRoot下,但不能是WEB-INF或META-INF下其及子目錄下

2.不允許被瀏覽器端訪問:

若放在工程下則放置在WEB-INF或META-INF及其子目錄下.

也可以不放在工程下

10. 檔案上傳問題-目錄分離

當我們上傳檔案過多,並且儲存在同一個目錄下時,我們就需要考慮怎樣處理它們,因為一個目錄下檔案過多,不僅降低效能,操作時也不方便。

為了防止同一個目錄下方上傳檔案數量過多

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目錄
}

11. 案例-新增商品資訊(上傳商品圖片)

1.匯入jar包

2.新增一個

3.在資料庫中新增一個欄位 String imgurl

Alter table products add imgurl varchar(100)

4.在javabean中新增一個欄位 String imgurl

5.add

二.檔案下載

1. 下載介紹與超連結下載

所謂的下載,其實就是將伺服器端的資源通過io流寫回到瀏覽器端。

超連結實現下載

如果檔案可以直接被瀏覽器解析,會直接在瀏覽器上開啟。

如果不能解析,可以下載

通過另存為進行下載

這種下載方式:當路徑提交時,會通過預設的servlet將檔案直接寫回到瀏覽器端

超連結下載問題分析

原因是在http響應頭中content-type,如果它的值可以被瀏覽器解析,那麼響應回的內容就會被瀏覽器端直接解析,如果content-type的值,不可以被瀏覽器直接解析,那麼就會下載。

注意:不能被瀏覽器直接訪問的檔案,不可以使用超連線下載.

2. 下載操作-伺服器端編碼實現下載

伺服器端編碼下載原理分析

通過response可以獲取輸出流,我們將要下載的資源,通過response獲取的輸出流直接寫回到瀏覽器端就可以。

伺服器端下載兩個響應頭設定(下載步驟)

1.通過response.setContentType()正設定響應資料的mimeType型別.

獲取一個檔案的mimeType型別

ServletContext.getMimeType(String filename);

2.再新增一個頭資訊,下載的時候就可以彈出一個提示框了

response.setHeader("Content-Disposition","attachment;filename=下載檔名");

3. 關於下載時亂碼問題分析與解決

亂碼分析:

對於下載時,我們在顯示下載檔名稱時,如果包含了中文,就可能出現亂碼問題,出現的原因,是對於不同的瀏覽器,它們在處理下載檔案時的編碼不一致,ie瀏覽器使用的是utf-8編碼,而firefox瀏覽器使用的是base64編碼

解決方案1:根據瀏覽器型別

下載檔名稱中文亂碼問題

response.setHeader("content-disposition", "attachment;filename="+ filename); 這段程式碼中的filename是指定下載檔案時的名稱

對於IE瀏覽器、谷歌,它要求必須給一個UTF-8碼

對於firefox瀏覽器,它要求必須給一個base64編碼

程式碼如下:
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 {
// 其它瀏覽器
filename = URLEncoder.encode(filename, "utf-8");
}

解決方案2:簡單處理

response.setHeader( "Content-Disposition", "attachment;filename=" + new String( fileName.getBytes("gb2312"), "ISO8859-1" ) );

4. 案例-下載銷售榜單分析

我們下載的檔案的格式是csv.它是一種以”.”來分隔的檔案,可以使用excel開啟

Excel要求它的檔案內容必須是gbk(gb2312)(gb18030),也就是我們可以通過response.setCharacterEncoding(“gbk”)來設定響應資料的編碼

5. 案例-下載銷售榜單實現

servlet寫法:

sql的寫法

SELECT
products.name,SUM(buynum) total
FROM
orders,orderitem,products
WHERE
orders.id=orderitem.order_id
AND
orderitem.product_id=products.id
AND
orders.paystat=1
GROUP BY
products.name
ORDER BY
total DESC;