在Web應用系統開發中,文件上傳和下載功能是非常常用的功能,今天來講一下JavaWeb中的文件上傳和下載功能的實現。
對于文件上傳,瀏覽器在上傳的過程中是將文件以流的形式提交到服務器端的,如果直接使用Servlet獲取上傳文件的輸入流然后再解析里面的請求參數是比較麻煩,所以一般選擇采用apache的開源工具common-fileupload這個文件上傳組件。這個common-fileupload上傳組件的jar包可以去apache官網上面下載,也可以在struts的lib文件夾下面找到,struts上傳的功能就是基于這個實現的。common-fileupload是依賴于common-io這個包的,所以還需要下載這個包。
創建一個FileUploadAndDownLoad項目,加入Apache的commons-fileupload文件上傳組件的相關Jar包,如下圖所示:
upload.jsp頁面的代碼如下:
1 <%@ page language="java" pageEncoding="UTF-8"%> 2 <!DOCTYPE HTML> 3 <html> 4 <head> 5 <title>文件上傳</title> 6 </head> 7 8 <body> 9 <form action="${pageContext.request.contextPath}/servlet/UploadHandleServlet" enctype="multipart/form-data" method="post">10 上傳用戶:<input type="text" name="username"><br/>11 上傳文件1:<input type="file" name="file1"><br/>12 上傳文件2:<input type="file" name="file2"><br/>13 <input type="submit" value="提交">14 </form>15 </body>16 </html>message.jsp的代碼如下:
1 <%@ page language="java" pageEncoding="UTF-8"%> 2 <!DOCTYPE HTML> 3 <html> 4 <head> 5 <title>消息提示</title> 6 </head> 7 8 <body> 9 ${message}10 </body>11 </html>2.2、處理文件上傳的Servlet
UploadHandleServlet的代碼如下:
1 package me.gacl.web.controller; 2 3 import java.io.File; 4 import java.io.FileOutputStream; 5 import java.io.IOException; 6 import java.io.InputStream; 7 import java.util.List; 8 import javax.servlet.ServletException; 9 import javax.servlet.http.HttpServlet; 10 import javax.servlet.http.HttpServletRequest; 11 import javax.servlet.http.HttpServletResponse; 12 import org.apache.commons.fileupload.FileItem; 13 import org.apache.commons.fileupload.disk.DiskFileItemFactory; 14 import org.apache.commons.fileupload.servlet.ServletFileUpload; 15 16 public class UploadHandleServlet extends HttpServlet { 17 18 public void doGet(HttpServletRequest request, HttpServletResponse response) 19 throws ServletException, IOException { 20 //得到上傳文件的保存目錄,將上傳的文件存放于WEB-INF目錄下,不允許外界直接訪問,保證上傳文件的安全 21 String savePath = this.getServletContext().getRealPath("/WEB-INF/upload"); 22 File file = new File(savePath); 23 //判斷上傳文件的保存目錄是否存在 24 if (!file.exists() && !file.isDirectory()) { 25 System.out.PRintln(savePath+"目錄不存在,需要創建"); 26 //創建目錄 27 file.mkdir(); 28 } 29 //消息提示 30 String message = ""; 31 try{ 32 //使用Apache文件上傳組件處理文件上傳步驟: 33 //1、創建一個DiskFileItemFactory工廠 34 DiskFileItemFactory factory = new DiskFileItemFactory(); 35 //2、創建一個文件上傳解析器 36 ServletFileUpload upload = new ServletFileUpload(factory); 37 //解決上傳文件名的中文亂碼 38 upload.setHeaderEncoding("UTF-8"); 39 //3、判斷提交上來的數據是否是上傳表單的數據 40 if(!ServletFileUpload.isMultipartContent(request)){ 41 //按照傳統方式獲取數據 42 return; 43 } 44 //4、使用ServletFileUpload解析器解析上傳數據,解析結果返回的是一個List<FileItem>集合,每一個FileItem對應一個Form表單的輸入項 45 List<FileItem> list = upload.parseRequest(request); 46 for(FileItem item : list){ 47 //如果fileitem中封裝的是普通輸入項的數據 48 if(item.isFormField()){ 49 String name = item.getFieldName(); 50 //解決普通輸入項的數據的中文亂碼問題 51 String value = item.getString("UTF-8"); 52 //value = new String(value.getBytes("iso8859-1"),"UTF-8"); 53 System.out.println(name + "=" + value); 54 }else{//如果fileitem中封裝的是上傳文件 55 //得到上傳的文件名稱, 56 String filename = item.getName(); 57 System.out.println(filename); 58 if(filename==null || filename.trim().equals("")){ 59 continue; 60 } 61 //注意:不同的瀏覽器提交的文件名是不一樣的,有些瀏覽器提交上來的文件名是帶有路徑的,如: c:/a/b/1.txt,而有些只是單純的文件名,如:1.txt 62 //處理獲取到的上傳文件的文件名的路徑部分,只保留文件名部分 63 filename = filename.substring(filename.lastIndexOf("//")+1); 64 //獲取item中的上傳文件的輸入流 65 InputStream in = item.getInputStream(); 66 //創建一個文件輸出流 67 FileOutputStream out = new FileOutputStream(savePath + "//" + filename); 68 //創建一個緩沖區 69 byte buffer[] = new byte[1024]; 70 //判斷輸入流中的數據是否已經讀完的標識 71 int len = 0; 72 //循環將輸入流讀入到緩沖區當中,(len=in.read(buffer))>0就表示in里面還有數據 73 while((len=in.read(buffer))>0){ 74 //使用FileOutputStream輸出流將緩沖區的數據寫入到指定的目錄(savePath + "//" + filename)當中 75 out.write(buffer, 0, len); 76 } 77 //關閉輸入流 78 in.close(); 79 //關閉輸出流 80 out.close(); 81 //刪除處理文件上傳時生成的臨時文件 82 item.delete(); 83 message = "文件上傳成功!"; 84 } 85 } 86 }catch (Exception e) { 87 message= "文件上傳失?。?quot;; 88 e.printStackTrace(); 89 90 } 91 request.setAttribute("message",message); 92 request.getRequestDispatcher("/message.jsp").forward(request, response); 93 } 94 95 public void doPost(HttpServletRequest request, HttpServletResponse response) 96 throws ServletException, IOException { 97 98 doGet(request, response); 99 }100 }在Web.xml文件中注冊UploadHandleServlet
1 <servlet>2 <servlet-name>UploadHandleServlet</servlet-name>3 <servlet-class>me.gacl.web.controller.UploadHandleServlet</servlet-class>4 </servlet>5 6 <servlet-mapping>7 <servlet-name>UploadHandleServlet</servlet-name>8 <url-pattern>/servlet/UploadHandleServlet</url-pattern>9 </servlet-mapping>運行效果如下:
文件上傳成功之后,上傳的文件保存在了WEB-INF目錄下的upload目錄,如下圖所示:
2.3、文件上傳的細節
上述的代碼雖然可以成功將文件上傳到服務器上面的指定目錄當中,但是文件上傳功能有許多需要注意的小細節問題,以下列出的幾點需要特別注意的
1、為保證服務器安全,上傳文件應該放在外界無法直接訪問的目錄下,比如放于WEB-INF目錄下。
2、為防止文件覆蓋的現象發生,要為上傳文件產生一個唯一的文件名。
3、為防止一個目錄下面出現太多文件,要使用hash算法打散存儲。
4、要限制上傳文件的最大值。
5、要限制上傳文件的類型,在收到上傳文件名時,判斷后綴名是否合法。
針對上述提出的5點細節問題,我們來改進一下UploadHandleServlet,改進后的代碼如下:
1 package me.gacl.web.controller; 2 3 import java.io.File; 4 import java.io.FileOutputStream; 5 import java.io.IOException; 6 import java.io.InputStream; 7 import java.util.List; 8 import java.util.UUID; 9 10 import javax.servlet.ServletException; 11 import javax.servlet.http.HttpServlet; 12 import javax.servlet.http.HttpServletRequest; 13 import javax.servlet.http.HttpServletResponse; 14 import org.apache.commons.fileupload.FileItem; 15 import org.apache.commons.fileupload.FileUploadBase; 16 import org.apache.commons.fileupload.ProgressListener; 17 import org.apache.commons.fileupload.disk.DiskFileItemFactory; 18 import org.apache.commons.fileupload.servlet.ServletFileUpload; 19 20 /** 21 * @ClassName: UploadHandleServlet 22 * @Description: TODO(這里用一句話描述這個類的作用) 23 * @author: 孤傲蒼狼 24 * @date: 2015-1-3 下午11:35:50 25 * 26 */ 27 public class UploadHandleServlet extends HttpServlet { 28 29 public void doGet(HttpServletRequest request, HttpServletResponse response) 30 throws ServletException, IOException { 31 //得到上傳文件的保存目錄,將上傳的文件存放于WEB-INF目錄下,不允許外界直接訪問,保證上傳文件的安全 32 String savePath = this.getServletContext().getRealPath("/WEB-INF/upload"); 33 //上傳時生成的臨時文件保存目錄 34 String tempPath = this.getServletContext().getRealPath("/WEB-INF/temp"); 35 File tmpFile = new File(tempPath); 36 if (!tmpFile.exists()) { 37 //創建臨時目錄 38 tmpFile.mkdir(); 39 } 40 41 //消息提示 42 String message = ""; 43 try{ 44 //使用Apache文件上傳組件處理文件上傳步驟: 45 //1、創建一個DiskFileItemFactory工廠 46 DiskFileItemFactory factory = new DiskFileItemFactory(); 47 //設置工廠的緩沖區的大小,當上傳的文件大小超過緩沖區的大小時,就會生成一個臨時文件存放到指定的臨時目錄當中。 48 factory.setSizeThreshold(1024*100);//設置緩沖區的大小為100KB,如果不指定,那么緩沖區的大小默認是10KB 49 //設置上傳時生成的臨時文件的保存目錄 50 factory.setRepository(tmpFile); 51 //2、創建一個文件上傳解析器 52 ServletFileUpload upload = new ServletFileUpload(factory); 53 //監聽文件上傳進度 54 upload.setProgressListener(new ProgressListener(){ 55 public void update(long pBytesRead, long pContentLength, int arg2) { 56 System.out.println("文件大小為:" + pContentLength + ",當前已處理:" + pBytesRead); 57 /** 58 * 文件大小為:14608,當前已處理:4096 59 文件大小為:14608,當前已處理:7367 60 文件大小為:14608,當前已處理:11419 61 文件大小為:14608,當前已處理:14608 62 */ 63 } 64 }); 65 //解決上傳文件名的中文亂碼 66 upload.setHeaderEncoding("UTF-8"); 67 //3、判斷提交上來的數據是否是上傳表單的數據 68 if(!ServletFileUpload.isMultipartContent(request)){ 69 //按照傳統方式獲取數據 70 return; 71 } 72 73 //設置上傳單個文件的大小的最大值,目前是設置為1024*1024字節,也就是1MB 74 upload.setFileSizeMax(1024*1024); 75 //設置上傳文件總量的最大值,最大值=同時上傳的多個文件的大小的最大值的和,目前設置為10MB 76 upload.setSizeMax(1024*1024*10); 77 //4、使用ServletFileUpload解析器解析上傳數據,解析結果返回的是一個List<FileItem>集合,每一個FileItem對應一個Form表單的輸入項 78 List<FileItem> list = upload.parseRequest(request); 79 for(FileItem item : list){ 80 //如果fileitem中封裝的是普通輸入項的數據 81 if(item.isFormField()){ 82 String name = item.getFieldName(); 83 //解決普通輸入項的數據的中文亂碼問題 84 String value = item.getString("UTF-8"); 85 //value = new String(value.getBytes("iso8859-1"),"UTF-8"); 86 System.out.println(name + "=" + value); 87 }else{//如果fileitem中封裝的是上傳文件 88 //得到上傳的文件名稱, 89 String filename = item.getName(); 90 System.out.println(filename); 91 if(filename==null || filename.trim().equals("")){ 92 continue; 93 } 94 //注意:不同的瀏覽器提交的文件名是不一樣的,有些瀏覽器提交上來的文件名是帶有路徑的,如: c:/a/b/1.txt,而有些只是單純的文件名,如:1.txt 95 //處理獲取到的上傳文件的文件名的路徑部分,只保留文件名部分 96 filename = filename.substring(filename.lastIndexOf("//")+1); 97 //得到上傳文件的擴展名 98 String fileExtName = filename.substring(filename.lastIndexOf(".")+1); 99 //如果需要限制上傳的文件類型,那么可以通過文件的擴展名來判斷上傳的文件類型是否合法100 System.out.println("上傳的文件的擴展名是:"+fileExtName);101 //獲取item中的上傳文件的輸入流102 InputStream in = item.getInputStream();103 //得到文件保存的名稱104 String saveFilename = makeFileName(filename);105 //得到文件的保存目錄106 String realSavePath = makePath(saveFilename, savePath);107 //創建一個文件輸出流108 FileOutputStream out = new FileOutputStream(realSavePath + "//" + saveFilename);109 //創建一個緩沖區110 byte buffer[] = new byte[1024];111 //判斷輸入流中的數據是否已經讀完的標識112 int len = 0;113 //循環將輸入流讀入到緩沖區當中,(len=in.read(buffer))>0就表示in里面還有數據114 while((len=in.read(buffer))>0){115 //使用FileOutputStream輸出流將緩沖區的數據寫入到指定的目錄(savePath + "//" + filename)當中116 out.write(buffer, 0, len);117 }118 //關閉輸入流119 in.close();120 //關閉輸出流121 out.close();122 //刪除處理文件上傳時生成的臨時文件123 //item.delete();124 message = "文件上傳成功!";125 }126 }127 }catch (FileUploadBase.FileSizeLimitExceededException e) {128 e.printStackTrace();129 request.setAttribute("message", "單個文件超出最大值?。?!");130 request.getRequestDispatcher("/message.jsp").forward(request, response);131 return;132 }catch (FileUploadBase.SizeLimitExceededException e) {133 e.printStackTrace();134 request.setAttribute("message", "上傳文件的總的大小超出限制的最大值?。?!");135 request.getRequestDispatcher("/message.jsp").forward(request, response);136 return;137 }catch (Exception e) {138 message= "文件上傳失??!";139 e.printStackTrace();140 }141 request.setAttribute("message",message);142 request.getRequestDispatcher("/message.jsp").forward(request, response);143 }144 145 /**146 * @Method: makeFileName147 * @Description: 生成上傳文件的文件名,文件名以:uuid+"_"+文件的原始名稱148 * @Anthor:孤傲蒼狼149 * @param filename 文件的原始名稱150 * @return uuid+"_"+文件的原始名稱151 */ 152 private String makeFileName(String filename){ //2.jpg153 //為防止文件覆蓋的現象發生,要為上傳文件產生一個唯一的文件名154 return UUID.randomUUID().toString() + "_" + filename;155 }156 157 /**158 * 為防止一個目錄下面出現太多文件,要使用hash算法打散存儲159 * @Method: makePath160 * @Description: 161 * @Anthor:孤傲蒼狼162 *163 * @param filename 文件名,要根據文件名生成存儲目錄164 * @param savePath 文件存儲路徑165 * @return 新的存儲目錄166 */ 167 private String makePath(String filename,String savePath){168 //得到文件名的hashCode的值,得到的就是filename這個字符串對象在內存中的地址169 int hashcode = filename.hashCode();170 int dir1 = hashcode&0xf; //0--15171 int dir2 = (hashcode&0xf0)>>4; //0-15172 //構造新的保存目錄173 String dir = savePath + "//" + dir1 + "//" + dir2; //upload/2/3 upload/3/5174 //File既可以代表文件也可以代表目錄175 File file = new File(dir);176 //如果目錄不存在177 if(!file.exists()){178 //創建目錄179 file.mkdirs();180 }181 return dir;182 }183 184 public void doPost(HttpServletRequest request, HttpServletResponse response)185 throws ServletException, IOException {186 187 doGet(request, response);188 }189 }針對上述提出的5點小細節問題進行改進之后,我們的文件上傳功能就算是做得比較完善了。
三、文件下載
3.1、列出提供下載的文件資源
我們要將Web應用系統中的文件資源提供給用戶進行下載,首先我們要有一個頁面列出上傳文件目錄下的所有文件,當用戶點擊文件下載超鏈接時就進行下載操作,編寫一個ListFileServlet,用于列出Web應用系統中所有下載文件。
ListFileServlet的代碼如下:
1 package me.gacl.web.controller; 2 3 import java.io.File; 4 import java.io.IOException; 5 import java.util.HashMap; 6 import java.util.Map; 7 import javax.servlet.ServletException; 8 import javax.servlet.http.HttpServlet; 9 import javax.servlet.http.HttpServletRequest;10 import javax.servlet.http.HttpServletResponse;11 12 /**13 * @ClassName: ListFileServlet14 * @Description: 列出Web系統中所有下載文件15 * @author: 孤傲蒼狼16 * @date: 2015-1-4 下午9:54:4017 *18 */ 19 public class ListFileServlet extends HttpServlet {20 21 public void doGet(HttpServletRequest request, HttpServletResponse response)22 throws ServletException, IOException {23 //獲取上傳文件的目錄24 String uploadFilePath = this.getServletContext().getRealPath("/WEB-INF/upload");25 //存儲要下載的文件名26 Map<String,String> fileNameMap = new HashMap<String,String>();27 //遞歸遍歷filepath目錄下的所有文件和目錄,將文件的文件名存儲到map集合中28 listfile(new File(uploadFilePath),fileNameMap);//File既可以代表一個文件也可以代表一個目錄29 //將Map集合發送到listfile.jsp頁面進行顯示30 request.setAttribute("fileNameMap", fileNameMap);31 request.getRequestDispatcher("/listfile.jsp").forward(request, response);32 }33 34 /**35 * @Method: listfile36 * @Description: 遞歸遍歷指定目錄下的所有文件37 * @Anthor:孤傲蒼狼38 * @param file 即代表一個文件,也代表一個文件目錄39 * @param map 存儲文件名的Map集合40 */ 41 public void listfile(File file,Map<String,String> map){42 //如果file代表的不是一個文件,而是一個目錄43 if(!file.isFile()){44 //列出該目錄下的所有文件和目錄45 File files[] = file.listFiles();46 //遍歷files[]數組47 for(File f : files){48 //遞歸49 listfile(f,map);50 }51 }else{52 /**53 * 處理文件名,上傳后的文件是以uuid_文件名的形式去重新命名的,去除文件名的uuid_部分54 file.getName().indexOf("_")檢索字符串中第一次出現"_"字符的位置,如果文件名類似于:9349249849-88343-8344_阿_凡_達.avi55 那么file.getName().substring(file.getName().indexOf("_")+1)處理之后就可以得到阿_凡_達.avi部分56 */57 String realName = file.getName().substring(file.getName().indexOf("_")+1);58 //file.getName()得到的是文件的原始名稱,這個名稱是唯一的,因此可以作為key,realName是處理過后的名稱,有可能會重復59 map.put(file.getName(), realName);60 }61 }62 63 public void doPost(HttpServletRequest request, HttpServletResponse response)64 throws ServletException, IOException {65 doGet(request, response);66 }67 }這里簡單說一下ListFileServlet中listfile方法,listfile方法是用來列出目錄下的所有文件的,listfile方法內部用到了遞歸,在實際開發當中,我們肯定會在數據庫創建一張表,里面會存儲上傳的文件名以及文件的具體存放目錄,我們通過查詢表就可以知道文件的具體存放目錄,是不需要用到遞歸操作的,這個例子是因為沒有使用數據庫存儲上傳的文件名和文件的具體存放位置,而上傳文件的存放位置又使用了散列算法打散存放,所以需要用到遞歸,在遞歸時,將獲取到的文件名存放到從外面傳遞到listfile方法里面的Map集合當中,這樣就可以保證所有的文件都存放在同一個Map集合當中。
在Web.xml文件中配置ListFileServlet
1 <servlet>2 <servlet-name>ListFileServlet</servlet-name>3 <servlet-class>me.gacl.web.controller.ListFileServlet</servlet-class>4 </servlet>5 6 <servlet-mapping>7 <servlet-name>ListFileServlet</servlet-name>8 <url-pattern>/servlet/ListFileServlet</url-pattern>9 </servlet-mapping>展示下載文件的listfile.jsp頁面如下:
1 <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> 2 <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> 3 <!DOCTYPE HTML> 4 <html> 5 <head> 6 <title>下載文件顯示頁面</title> 7 </head> 8 9 <body>10 <!-- 遍歷Map集合 -->11 <c:forEach var="me" items="${fileNameMap}">12 <c:url value="/servlet/DownLoadServlet" var="downurl">13 <c:param name="filename" value="${me.key}"></c:param>14 </c:url>15 ${me.value}<a href="${downurl}">下載</a>16 <br/>17 </c:forEach>18 </body>19 </html>訪問ListFileServlet,就可以在listfile.jsp頁面中顯示提供給用戶下載的文件資源,如下圖所示:
3.2、實現文件下載
編寫一個用于處理文件下載的Servlet,DownLoadServlet的代碼如下:
1 package me.gacl.web.controller; 2 3 import java.io.File; 4 import java.io.FileInputStream; 5 import java.io.IOException; 6 import java.io.OutputStream; 7 import java.net.URLEncoder; 8 import javax.servlet.ServletException; 9 import javax.servlet.http.HttpServlet;10 import javax.servlet.http.HttpServletRequest;11 import javax.servlet.http.HttpServletResponse;12 13 public class DownLoadServlet extends HttpServlet {14 15 16 public void doGet(HttpServletRequest request, HttpServletResponse response)17 throws ServletException, IOException {18 //得到要下載的文件名19 String fileName = request.getParameter("filename"); //23239283-92489-阿凡達.avi20 fileName = new String(fileName.getBytes("iso8859-1"),"UTF-8");21 //上傳的文件都是保存在/WEB-INF/upload目錄下的子目錄當中22 String fileSaveRootPath=this.getServletContext().getRealPath("/WEB-INF/upload");23 //通過文件名找出文件的所在目錄24 String path = findFileSavePathByFileName(fileName,fileSaveRootPath);25 //得到要下載的文件26 File file = new File(path + "//" + fileName);27 //如果文件不存在28 if(!file.exists()){29 request.setAttribute("message", "您要下載的資源已被刪除??!");30 request.getRequestDispatcher("/message.jsp").forward(request, response);31 return;32 }33 //處理文件名34 String realname = fileName.substring(fileName.indexOf("_")+1);35 //設置響應頭,控制瀏覽器下載該文件36 response.setHeader("content-disposition", "attachment;filename=" + URLEncoder.encode(realname, "UTF-8"));37 //讀取要下載的文件,保存到文件輸入流38 FileInputStream in = new FileInputStream(path + "//" + fileName);39 //創建輸出流40 OutputStream out = response.getOutputStream();41 //創建緩沖區42 byte buffer[] = new byte[1024];43 int len = 0;44 //循環將輸入流中的內容讀取到緩沖區當中45 while((len=in.read(buffer))>0){46 //輸出緩沖區的內容到瀏覽器,實現文件下載47 out.write(buffer, 0, len);48 }49 //關閉文件輸入流50 in.close();51 //關閉輸出流52 out.close();53 }54 55 /**56 * @Method: findFileSavePathByFileName57 * @Description: 通過文件名和存儲上傳文件根目錄找出要下載的文件的所在路徑58 * @Anthor:孤傲蒼狼59 * @param filename 要下載的文件名60 * @param saveRootPath 上傳文件保存的根目錄,也就是/WEB-INF/upload目錄61 * @return 要下載的文件的存儲目錄62 */ 63 public String findFileSavePathByFileName(String filename,String saveRootPath){64 int hashcode = filename.hashCode();65 int dir1 = hashcode&0xf; //0--1566 int dir2 = (hashcode&0xf0)>>4; //0-1567 String dir = saveRootPath + "//" + dir1 + "//" + dir2; //upload/2/3 upload/3/568 File file = new File(dir);69 if(!file.exists()){70 //創建目錄71 file.mkdirs();72 }73 return dir;74 }75 76 public void doPost(HttpServletRequest request, HttpServletResponse response)77 throws ServletException, IOException {78 doGet(request, response);79 }80 }在Web.xml文件中配置DownLoadServlet
1 <servlet>2 <servlet-name>DownLoadServlet</servlet-name>3 <servlet-class>me.gacl.web.controller.DownLoadServlet</servlet-class>4 </servlet>5 6 <servlet-mapping>7 <servlet-name>DownLoadServlet</servlet-name>8 <url-pattern>/servlet/DownLoadServlet</url-pattern>9 </servlet-mapping>點擊【下載】超鏈接,將請求提交到DownLoadServlet就行處理就可以實現文件下載了,運行效果如下圖所示:
從運行結果可以看到,我們的文件下載功能已經可以正常下載文件了。
關于JavaWeb中的文件上傳和下載功能的內容就這么多。
新聞熱點
疑難解答