一、課程概述
在Web應用系統開發中,文件上傳功能是非常常用的功能,今天來主要講講JavaWeb中的文件上傳功能的相關技術實現,並且隨著互聯網技術的飛速發展,用戶對網站的體驗要求越來越高,在文件上傳功能的技術上也出現許多創新點,例如異步上傳文件,拖拽式上傳,黏貼上傳,上傳進度監控,文件縮略圖,大文件斷點續傳,大文件秒傳等等。
本課程需要的基礎知識:
了解基本的Http協議內容
基本IO流操作技術
Servlet基礎知識
javascript/jQuery技術基礎知識
二、文件上傳的基礎
對於文件上傳,瀏覽器在上傳的過程中是將文件以流的形式提交到服務器端的,並且所有流數據都會隨著Http請求攜帶到服務器端。所以,文件上傳時的請求內容格式要能夠基本看懂。
文件上傳頁面:
<form action="/itheimaUpload/UploadServlet" method="post" enctype="multipart/form-data">請選擇上傳的文件:<input type="file" name="attach"/><br/><input type="submit" value="提交"/></form>
Http請求內容:
三、Java後台使用Servlet接收文件
如果使用Servlet獲取上傳文件的輸入流然後再解析裡面的請求參數是比較麻煩,所以一般後台選擇採用Apache的開源工具common-fileupload這個文件上傳組件。
//Java後台代碼:Commons-fileUpload組件上傳文件public class UploadServlet extends HttpServlet {public void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {//1.配置緩存DiskFileItemFactory factory = new DiskFileItemFactory(1*1024*1024,new File("c:/tempFiles/"));//2.創建ServleFileUpload對象ServletFileUpload sfu = new ServletFileUpload(factory);//解決文件名稱中文問題sfu.setHeaderEncoding("utf-8");//3.解析try {List<FileItem> list = sfu.parseRequest(request);//解析所有內容if(list!=null){for(FileItem item:list){//判斷是否為普通表單參數if(item.isFormField()){//普通表單參數//獲取表單的name屬性名稱String fieldName = item.getFieldName();//獲取表單參數值String value = item.getString("utf-8"); }else{//文件if(item.getName()!=null && !item.getName().equals("")) {//保存到服務器硬盤FileUtils.copyInputStreamToFile(item.getInputStream(), new File("c:/targetFiles/"+item.getName()));item.delete();}}}}} catch (FileUploadException e) {e.printStackTrace();}}public void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {doGet(request, response);}}四、使用WebUploader上傳組件
文件上傳頁面的前端我們可以選擇使用一些比較好用的上傳組件,例如百度的開源組件WebUploader,這個組件基本能滿足文件上傳的一些日常所需功能,如異步上傳文件,拖拽式上傳,黏貼上傳,上傳進度監控,文件縮略圖,甚至是大文件斷點續傳,大文件秒傳。
下載WebUpload組件
http://fex.baidu.com/webuploader/ 到WebUpload官網下載WebUpload包
WebUpload目錄結構:
基本文件上傳Demo(包含上傳進度)
前端
1.1 在頁面導入所需css,js
<link rel="stylesheet" type="text/css"href="${pageContext.request.contextPath}/css/webuploader.css"><script type="text/javascript"src="${pageContext.request.contextPath }/js/jquery-1.10.2.min.js"></script><script type="text/javascript"src="${pageContext.request.contextPath }/js/webuploader.js"></script>1.2 編寫上傳頁面標籤
<!-- 上傳div --><div id="uploader"><!-- 顯示文件列表信息--><ul id="fileList"></ul><!-- 選擇文件區域--><div id="filePicker">點擊選擇文件</div></div>
1.3 編寫webupload代碼
<script type="text/javascript">//1.初始化WebUpload,以及配置全局的參數var uploader = WebUploader.create({//flashk控件的地址swf: "${pageContext.request.contextPath}/js/Uploader.swf",//後台提交地址server:"${pageContext.request.contextPath}/UploadServlet",//選擇文件控件的標籤pick:"#filePicker",//自動上傳文件auto:true,});//2.選擇文件後,文件信息隊列展示// 註冊fileQueued事件:當文件加入隊列後觸發// file: 代表當前選擇的文件uploader.on("fileQueued",function(file){//追加文件信息div$("#fileList").append("<div id='"+file.id+"' class='fileInfo'><span>"+file.name+"</span><div class='state'>等待上傳...</div><span class='text'></span></div>");});//3.註冊上傳進度監聽//file: 正在上傳的文件//percentage: 當前進度的比例。最大為1.例如:0.2uploader.on("uploadProgress",function(file,percentage){var id = $("#"+file.id);//更新狀態信息id.find("div.state").text("上傳中...");//更新上傳百分比id.find("span.text").text(Math.round(percentage*100)+"%");});//4.註冊上傳完畢監聽//file:上傳完畢的文件//response:後台回送的數據,以json格式返回uploader.on("uploadSuccess",function(file,response){//更新狀態信息$("#"+file.id).find("div.state").text("上傳完畢");});2)後端Servlet代碼
DiskFileItemFactory factory = new DiskFileItemFactory();ServletFileUpload sfu = new ServletFileUpload(factory);sfu.setHeaderEncoding("utf-8");try {List<FileItem> items = sfu.parseRequest(request);for(FileItem item:items){if(item.isFormField()){//普通信息}else{//文件信息//判斷只有文件才需要進行保存處理System.out.println("接收的文件名稱:"+item.getName());//拷貝文件到後台的硬盤FileUtils.copyInputStreamToFile(item.getInputStream(), new File(serverPath+"/"+item.getName()));System.out.println("文件保存成功");}}} catch (FileUploadException e) {e.printStackTrace();}生成圖片縮略圖
關鍵點:調用uploader.makeThumb()方法生成縮略圖
uploader.on("fileQueued",function(file){//追加文件信息div$("#fileList").append("<div id='"+file.id+"' class='fileInfo'><img/><span>"+file.name+"</span><div class='state'>等待上傳...</div><span class='text'></span></div>");//製造圖片縮略圖:調用makeThumb()方法//error: 製造縮略圖失敗//src: 縮略圖的路徑uploader.makeThumb(file,function(error,src){var id = $("#"+file.id);//如果失敗,則顯示“不能預覽”if(error){id.find("img").replaceWith("不能預覽");} //成功,則顯示縮略圖到指定位置id.find("img").attr("src",src); });});拖拽,黏貼上傳
1)頁面添加拖拽區域的div
<!-- 上傳div --><div id="uploader"><!-- 文件拖拽區域--><div id="dndArea"><p>將文件直接拖拽到這裡即可自動上傳</p></div><!-- 顯示文件列表信息--><ul id="fileList"></ul><!-- 選擇文件區域--><div id="filePicker">點擊選擇文件</div></div>
2)在webuploader的全局配置參數添加拖拽功能的參數
//1.初始化WebUpload,以及配置全局的參數var uploader = WebUploader.create({//flashk控件的地址swf: "${pageContext.request.contextPath}/js/Uploader.swf",//後台提交地址server:"${pageContext.request.contextPath}/UploadServlet",//選擇文件控件的標籤pick:"#filePicker",//自動上傳文件auto:true,//開啟拖拽功能,指定拖拽區域dnd:"#dndArea",//禁用頁面其他地方的拖拽功能,防止頁面直接打開文件disableGlobalDnd:true//開啟黏貼功能paste:"#uploader"});大文件分塊上傳
1)在webuploader全局參數中添加分塊上傳參數
//1.初始化WebUpload,以及配置全局的參數var uploader = WebUploader.create({//flashk控件的地址swf: "${pageContext.request.contextPath}/js/Uploader.swf",//後台提交地址server:"${pageContext.request.contextPath}/UploadServlet",//選擇文件控件的標籤pick:"#filePicker",//自動上傳文件auto:true,//開啟拖拽功能,指定拖拽區域dnd:"#dndArea",//禁用頁面其他地方的拖拽功能,防止頁面直接打開文件disableGlobalDnd:true,//開啟黏貼功能paste:"#uploader",//分塊上傳設置//是否分塊上傳chunked:true,//每塊文件大小(默認5M)chunkSize:5*1024*1024,//開啟幾個並發線程(默認3個)threads:3,//在上傳當前文件時,準備好下一個文件prepareNextFile:true});2)監控上傳文件的三個時間點
添加以上三個配置後,會發現當文件超過5M時,webuploader自動把文件會分幾個請求發送給後台
每個分塊請求,包含的信息:
可以監聽文件分塊上傳的三個重要的時間點。
before-send-file : 在所有分塊發送之前調用before-send: 如果有分塊,在每個分塊發送之前調用after-send-file: 在所有分塊發送完成之後調用//5.監控文件上傳的三個時間點(注意:該段代碼必須放在WebUploader.create之前)//時間點1::所有分塊進行上傳之前(1.可以計算文件的唯一標記;2.可以判斷是否秒傳) //時間點2: 如果分塊上傳,每個分塊上傳之前(1.詢問後台該分塊是否已經保存成功,用於斷點續傳)//時間點3:所有分塊上傳成功之後(1.通知後台進行分塊文件的合併工作)WebUploader.Uploader.register({"before-send-file":"beforeSendFile","before-send":"beforeSend","after-send-file":"afterSendFile"},{//時間點1::所有分塊進行上傳之前調用此函數beforeSendFile:function(){//1.計算文件的唯一標記,用於斷點續傳和秒傳//2.請求後台是否保存過該文件,如果存在,則跳過該文件,實現秒傳功能},//時間點2:如果有分塊上傳,則每個分塊上傳之前調用此函數beforeSend:function(){//1.請求後台是否保存過當前分塊,如果存在,則跳過該分塊文件,實現斷點續傳功能},//時間點3:所有分塊上傳成功之後調用此函數afterSendFile:function(){//1.如果分塊上傳,則通過後台合併所有分塊文件}});before-send-file邏輯:
//利用md5File()方法計算文件的唯一標記符//該函數接收一個deferredbeforeSendFile:function(file){//創建一個defferedvar deferred = WebUploader.Deferred();//1.計算文件的唯一標記,用於斷點續傳和秒傳(new WebUploader.Uploader()).md5File(file,0,5*1024*1024).progress(function(percentage){$("#"+file.id).find("div.state").text("正在獲取文件信息...");}).then(function(val){uniqueFileTag = val;$("#"+file.id).find("div.state").text("成功獲取文件信息");//只有文件信息獲取成功,才進行下一步操作deferred.resolve();});//alert(uniqueFileTag);//2.請求後台是否保存過該文件,如果存在,則跳過該文件,實現秒傳功能//返回defferedreturn deferred.promise();}before-send邏輯:
//向後台發送當前文件的唯一標記,用於後台創建保存分塊文件的目錄beforeSend:function(){//攜帶當前文件的唯一標記到後台,用於讓後台創建保存該文件分塊的目錄this.owner.options.formData.fileMd5 = fileMd5;}3)後台需要保存所有分塊文件
//為每個文件創建一個目錄,並保存這個文件的所有分塊文件//判斷是否已經分塊上傳if(chunks!=null){System.out.println("分塊處理...");//進行分塊上傳了//建立一個臨時目錄,用於保存所有分塊文件File chunksDir = new File(serverPath+"/"+fileMd5);if(!chunksDir.exists()){chunksDir.mkdir();}if(chunk!=null){//保存分塊文件File chunkFile = new File(chunksDir.getPath()+"/"+chunk);FileUtils.copyInputStreamToFile(item.getInputStream(), chunkFile);}4)前台通知後台合併所有分塊文件
//前台通知後台合併文件after-send-file邏輯:afterSendFile:function(file){//1.如果分塊上傳,則通過後台合併所有分塊文件//請求後台合併文件$.ajax({type:"POST",url:"${pageContext.request.contextPath}/UploadCheckServlet?action=mergeChunks",data:{//文件唯一標記fileMd5:fileMd5,//文件名稱fileName:file.name},dataType:"json",success:function(response){alert(response.msg);}});}//後台合併所有分塊文件if("mergeChunks".equals(action)){System.out.println("開始合併文件...");//合併文件String fileMd5 = request.getParameter("fileMd5");String fileName = request.getParameter("fileName");//讀取目錄裡面的所有文件File f = new File(serverPath+"/"+fileMd5);File[] fileArray = f.listFiles(new FileFilter(){//排除目錄,只要文件public boolean accept(File pathname) {if(pathname.isDirectory()){return false;}return true;}});//轉成集合,便於排序List<File> fileList = new ArrayList<File>(Arrays.asList(fileArray));//從小到大排序Collections.sort(fileList, new Comparator<File>() {public int compare(File o1, File o2) {if(Integer.parseInt(o1.getName()) < Integer.parseInt(o2.getName())){return -1;}return 1;}});File outputFile = new File(serverPath+"/"+fileName);//創建文件outputFile.createNewFile();//輸出流FileChannel outChannel = new FileOutputStream(outputFile).getChannel();//合併FileChannel inChannel;for(File file : fileList){inChannel = new FileInputStream(file).getChannel();inChannel.transferTo(0, inChannel.size(), outChannel);inChannel.close();//刪除分片file.delete();}//清除文件夾File tempFile = new File(serverPath+"/"+fileMd5);if(tempFile.isDirectory() && tempFile.exists()){tempFile.delete();}//關閉流outChannel.close();response.setContentType("text/html;charset=utf-8");response.getWriter().write("{/"msg/":/"合併成功/"}");}大文件斷點續傳
在實現了分塊上傳的基礎上,實現斷點續傳就非常簡單了! ! !
前端:
//時間點2:如果有分塊上傳,則每個分塊上傳之前調用此函數//block:代表當前分塊對象beforeSend:function(block){//1.請求後台是否保存過當前分塊,如果存在,則跳過該分塊文件,實現斷點續傳功能var deferred = WebUploader.Deferred();//請求後台是否保存完成該文件信息,如果保存過,則跳過,如果沒有,則發送該分塊內容$.ajax({type:"POST",url:"${pageContext.request.contextPath}/UploadCheckServlet?action=checkChunk",data:{//文件唯一標記fileMd5:fileMd5,//當前分塊下標chunk:block.chunk,//當前分塊大小chunkSize:block.end-block.start},dataType:"json",success:function(response){if(response.ifExist){//分塊存在,跳過該分塊deferred.reject();}else{//分塊不存在或者不完整,重新發送該分塊內容deferred.resolve();}}});//攜帶當前文件的唯一標記到後台,用於讓後台創建保存該文件分塊的目錄this.owner.options.formData.fileMd5 = fileMd5;return deferred.promise(); },後台:
//檢查該分塊是否存在或者完整保存private void checkChunk(HttpServletRequest request,HttpServletResponse response) throws IOException,FileNotFoundException {System.out.println("checkChunk...");String fileMd5 = request.getParameter("fileMd5");String chunk = request.getParameter("chunk");String chunkSize = request.getParameter("chunkSize");File checkFile = new File(serverPath+"/"+fileMd5+"/"+chunk);response.setContentType("text/html;charset=utf-8");//檢查文件是否存在,且大小是否一致if(checkFile.exists() && checkFile.length()==Integer.parseInt(chunkSize)){response.getWriter().write("{/"ifExist/":1}");}else{response.getWriter().write("{/"ifExist/":0}");}}文件秒傳
在所有分塊請求之前,就已經可以進行實現秒傳功能! ! !
前端:
beforeSendFile:function(file){//創建一個defferedvar deferred = WebUploader.Deferred();//1.計算文件的唯一標記,用於斷點續傳和秒傳(new WebUploader.Uploader()).md5File(file,0,5*1024*1024).progress(function(percentage){$("#"+file.id).find("div.state").text("正在獲取文件信息...");}).then(function(val){fileMd5 = val;$("#"+file.id).find("div.state").text("成功獲取文件信息");//2.請求後台是否保存過該文件,如果存在,則跳過該文件,實現秒傳功能$.ajax({type:"POST",url:"${pageContext.request.contextPath}/UploadCheckServlet?action=fileCheck",data:{//文件唯一標記fileMd5:fileMd5},dataType:"json",success:function(response){if(response.ifExist){$("#"+file.id).find("div.state").text("秒傳成功"); //如果存在,則跳過該文件,秒傳成功deferred.reject();}else{//繼續上傳deferred.resolve();}}});});//返回defferedreturn deferred.promise();},後台:
//檢查文件的md5數據是否跟在數據庫存在private void fileCheck(HttpServletRequest request,HttpServletResponse response) throws IOException,FileNotFoundException {String fileMd5 = request.getParameter("fileMd5");//模擬數據庫Map<String,String> database = new HashMap<String,String>();database.put("576018603f4091782b68b78af85704a1", "01.課程回顧.itcast");response.setContentType("text/html;charset=utf-8");if(database.containsKey(fileMd5)){response.getWriter().write("{/"ifExist/":1}");}else{response.getWriter().write("{/"ifExist/":0}");}}以上所述是小編給大家介紹的JavaWeb文件上傳下載實例講解(酷炫的文件上傳技術),希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對武林網網站的支持!