SpingMVC框架:fileUpload组件原理和实现
QUESTION:fileUpload組件原理和實現
?
ANSWER:
?
目錄
QUESTION:fileUpload組件原理和實現
ANSWER:
一:異常產生
查詢了一系列博客后,發現這是由于上傳文件的大小超過了可以上傳的臨時文件限制。
二:fileUpload組件及Java Web中文件上傳的原理方法
2.1基本原理
2.2實現代碼
2.3重要方法
2.4注意事項:
?
一:異常產生
在剛學習springMVC框架時,上傳文件出現了以下HTTP Status 500 – Internal Server Error:
Exception
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.apache.commons.fileupload.FileUploadBase$IOFileUploadException: Processing of multipart/form-data request failed. C:\Program Files\apache-tomcat-8.5.38\temp\upload_2f146db1_be01_4311_a3d8_e6c7cd69c8d3_00000002.tmp (拒絕訪問。)org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1013)org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:908)javax.servlet.http.HttpServlet.service(HttpServlet.java:661)org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:882)javax.servlet.http.HttpServlet.service(HttpServlet.java:742)org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:200)org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)Root Cause
org.apache.commons.fileupload.FileUploadBase$IOFileUploadException: Processing of multipart/form-data request failed. C:\Program Files\apache-tomcat-8.5.38\temp\upload_2f146db1_be01_4311_a3d8_e6c7cd69c8d3_00000002.tmp (拒絕訪問。)org.apache.commons.fileupload.FileUploadBase.parseRequest(FileUploadBase.java:351)org.apache.commons.fileupload.servlet.ServletFileUpload.parseRequest(ServletFileUpload.java:115)com.xy.controller.UserController.fileUpload(UserController.java:36)sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)java.lang.reflect.Method.invoke(Method.java:498)org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:189)org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:138)org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:102)org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:892)org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:797)org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1038)org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:942)org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1005)org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:908)javax.servlet.http.HttpServlet.service(HttpServlet.java:661)org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:882)javax.servlet.http.HttpServlet.service(HttpServlet.java:742)org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:200)org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)Root Cause
java.io.FileNotFoundException: C:\Program Files\apache-tomcat-8.5.38\temp\upload_2f146db1_be01_4311_a3d8_e6c7cd69c8d3_00000002.tmp (拒絕訪問。)java.io.FileOutputStream.open0(Native Method)java.io.FileOutputStream.open(FileOutputStream.java:270)java.io.FileOutputStream.<init>(FileOutputStream.java:213)java.io.FileOutputStream.<init>(FileOutputStream.java:162)org.apache.commons.io.output.DeferredFileOutputStream.thresholdReached(DeferredFileOutputStream.java:178)org.apache.commons.io.output.ThresholdingOutputStream.checkThreshold(ThresholdingOutputStream.java:224)org.apache.commons.io.output.ThresholdingOutputStream.write(ThresholdingOutputStream.java:128)org.apache.commons.fileupload.util.Streams.copy(Streams.java:107)org.apache.commons.fileupload.util.Streams.copy(Streams.java:70)org.apache.commons.fileupload.FileUploadBase.parseRequest(FileUploadBase.java:347)org.apache.commons.fileupload.servlet.ServletFileUpload.parseRequest(ServletFileUpload.java:115)com.xy.controller.UserController.fileUpload(UserController.java:36)sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)java.lang.reflect.Method.invoke(Method.java:498)org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:189)org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:138)org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:102)org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:892)org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:797)org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1038)org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:942)org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1005)org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:908)javax.servlet.http.HttpServlet.service(HttpServlet.java:661)org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:882)javax.servlet.http.HttpServlet.service(HttpServlet.java:742)org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:200)org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)Note The full stack trace of the root cause is available in the server logs.
查詢了一系列博客后,發現這是由于上傳文件的大小超過了可以上傳的臨時文件限制。
?
二:fileUpload組件及Java Web中文件上傳的原理方法
2.1基本原理
給出官網文檔:upLoad。
組件FileUpload依賴于Commons IO組件,因此在繼續之前,要確保在你的工程classpath中有描述頁中提到的相應版本。(這里FileUpload版本為:commons- fileupload-1.2.1,Commons IO版本為:commons-io-1.4)。
最簡單的示例(官網):
// Create a factory for disk-based file items DiskFileItemFactory factory = new DiskFileItemFactory();// Configure a repository (to ensure a secure temp location is used) ServletContext servletContext = this.getServletConfig().getServletContext(); File repository = (File) servletContext.getAttribute("javax.servlet.context.tempdir"); factory.setRepository(repository);// Create a new file upload handler ServletFileUpload upload = new ServletFileUpload(factory);// Parse the request List<FileItem> items = upload.parseRequest(request);原理:
上傳的文件要求包括一個根據RFC 1867(在HTML中基于表單的文件)編碼的選項列表清單。組件FileUpload可以解析這個請求,并給你的應用程序提供一份獨立上傳的項目清單。無論每個項目背后如何執行都實現了FileItem接口。
這里將描述組件FileUpload庫的普通API,這些API比較簡單。不過,對于最終的實現,你可以參考最新的API流。
每一個文件項目都有一些屬性,這些可能在你的應用程序中應用到。比如:每一個項目有一個名稱name和內容類型content type,并提供了一個 InputStream訪問其數據。另一方面,你處理項目的方法可能有所不同,這個依賴于是否這個項目是一個規則的表單域,即:這個數據是來自普通的表單文本,還是普通的HTML域或是一個上傳文件。在FileItem接口中提供了處理這些問題的方法,可以更加方便的去訪問這些數據。
組件FileUpload使用FileItemFactory工廠創建新的文件項目。這個給了組件FileUpload很大的靈活性。這個工廠擁有怎樣創建項目的最終控制權。工廠執行過程中上傳項目文件的臨時數據可以存儲在內存中或硬盤上。這個依賴于上傳項目的大小(即:數據的字節)。不過這種行為可以在你的應用程序中適當的自定制。
解析:
在實現上傳項目之前,當然需要解析這個請求。確保這個請求的確是一個正確的上傳文件,組件FileUpload為了使這個判斷簡單,提供了一個靜態的方法去做這個事情。
// 檢測我們是否一個文件上傳的請求
boolean isMultipart = ServletFileUpload.isMultipartContent(request);
示例:
最簡單的使用情況如下:
* 上傳項目只要足夠小,就應該將其保存在內存中。
* 較大的項目應該被寫入到硬盤的臨時文件中。
* 應該避免有非常大的上傳項目。
* 設置項目默認的在內存中所占的空間,限制最大的上傳請求,并設定臨時文件 的位置。
處理這種情況下的請求非常的簡單:
// 創建磁盤工廠
FileItemFactory factory = new DiskFileItemFactory();
// 創建處理工具
ServletFileUpload upload = new ServletFileUpload(factory);
// 解析
List <FileItem> items = upload.parseRequest(request);
這就是我們真正需要的全部代碼。
處理的結果是生成了一個文件項目列表,每個文件項目實現一個FileItem接口。下面將介紹如何處理這些項目。
?
控制:
如果你的使用情況和上面描述的例子很接近,但是你需要在一點更多的控制限定文件的大小或臨時文件的存放位置。你可以很容易的自定義上傳實例或文件項目或兩者的行為。下面例子展示了幾種配置選項:
// 創建磁盤工廠
DiskFileItemFactory factory = new DiskFileItemFactory();
// 設置參數
factory.setSizeThreshold(yourMaxMemorySize);
factory.setRepository(yourTempDirectory);
// 創建處理工具
ServletFileUpload upload = new ServletFileUpload(factory);
// 設置最大允許的尺寸
upload.setSizeMax(yourMaxRequestSize);
// 解析
List <FileItem> items = upload.parseRequest(request);
當然,每一個配置處理方法都是獨立于其他方法的,但是如果你想一次性配置這個工廠,你可以使用工廠的另一個重載方法。像這樣:
DiskFileItemFactory factory = new DiskFileItemFactory( yourMaxMemorySize, yourTempDirectory);
詳細的接口文檔:http://commons.apache.org/proper/commons-fileupload/javadocs/api-release/index.html
2.2實現代碼
使用fileUpload固定步驟:
? ? 創建工廠類:DiskFileItemFactory factory=new DiskFileItemFactory();
? ? 創建解析器:ServletFileUpload upload=new ServletFileUpload(factory);
? ? 使用解析器解析request對象:List<FileItem> list=upload.parseRequest(request);
?
首先導入相關包。見上文。
1.jsp代碼:
<%--Created by IntelliJ IDEA.User: 楊路恒Date: 2019/8/17 0017Time: 11:24 --%> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head><title>文件上傳</title> </head> <body><form action="UserController/fileUpload1" method="post" enctype="multipart/form-data">選擇文件:<input type="file" name="upload"><br>上傳:<input type="submit" value="上傳" name="uploadFile"></form> </body> </html>2.配置springmvc-xml:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans"xmlns:mvc="http://www.springframework.org/schema/mvc"xmlns:context="http://www.springframework.org/schema/context"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/mvchttp://www.springframework.org/schema/mvc/spring-mvc.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd"><!--掃描spring的包--><context:component-scan base-package="com.xy"></context:component-scan><!--視圖解析器對象--><bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver"><property name="prefix" value="/WEB-INF/pages/"></property><property name="suffix" value=".jsp"></property></bean><!--前端控制器,哪些靜態資源不攔截--><mvc:resources location="/css/" mapping="/css/**"/> <!-- 樣式 --><mvc:resources location="/images/" mapping="/images/**"/> <!-- 圖片 --><mvc:resources location="/js/" mapping="/js/**"/> <!-- javascript --><!--開啟springMVC的支持,類型轉換器生效--><mvc:annotation-driven></mvc:annotation-driven></beans>3.配置web.xml:
<!DOCTYPE web-app PUBLIC"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN""http://java.sun.com/dtd/web-app_2_3.dtd" ><web-app><display-name>Archetype Created Web Application</display-name><servlet><servlet-name>dispatcherServlet</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><!--前端控制器--><init-param><param-name>contextConfigLocation</param-name><param-value>classpath:spring-mvc.xml</param-value></init-param><load-on-startup>1</load-on-startup></servlet><servlet-mapping><servlet-name>dispatcherServlet</servlet-name><url-pattern>/</url-pattern></servlet-mapping><!--中文亂碼過濾器--><filter><filter-name>characterEncodingFilter</filter-name><filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class><init-param><param-name>encoding</param-name><param-value>UTF-8</param-value></init-param></filter><filter-mapping><filter-name>characterEncodingFilter</filter-name><url-pattern>/*</url-pattern></filter-mapping> </web-app>4.控制器代碼:
package com.xy.controller;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.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping;import javax.servlet.http.HttpServletRequest; import java.io.File; import java.util.List;@Controller @RequestMapping("/UserController") public class UserController {/*** 文件上傳* @return*/@RequestMapping("/fileUpload1")public String fileUpload(HttpServletRequest request) throws Exception {System.out.println("文件上傳");//使用fileupload組件上傳文件//上傳路徑String path=request.getSession().getServletContext().getRealPath("/uploads");//判斷該路徑是否存在File file=new File(path);if (!file.exists()){file.mkdirs();}//解析request對象獲取上傳文件項DiskFileItemFactory diskFileItemFactory=new DiskFileItemFactory(); // diskFileItemFactory.setSizeThreshold(1024*1024); //設置內存緩沖區大小,系統默認值10KBFile temp=new File(request.getSession().getServletContext().getRealPath("/temp"));temp.mkdirs();System.out.println("建立成功");diskFileItemFactory.setRepository(temp);System.out.println(System.getProperty("java.io.tmpdir"));ServletFileUpload servletFileUpload=new ServletFileUpload(diskFileItemFactory);servletFileUpload.setFileSizeMax(100*1024*1024);//解析requestList<FileItem> items=servletFileUpload.parseRequest(request);for (FileItem item:items) {//判斷當前item是否是上傳文件對象if (item.isFormField()){//說明普通表單向}else {//說明上傳文件項//獲取文件名稱String fileName=item.getName();//完成文件上傳item.write(new File(path,fileName));System.out.println("寫入成功");//刪除臨時文件,系統會自動刪除 // item.delete();}}return "success";} }5.success代碼:
<%--Created by IntelliJ IDEA.User: 楊路恒Date: 2019/8/17 0017Time: 11:21 --%> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head><title>Title</title> </head> <body><h1>文件上傳成功</h1> </body> </html>1.普通表單提交默認enctype="application/x-www-form-urlencoded";但是當表單中存在文件類型時,需要設置enctype="multipart/form-data",它不對字符進行編碼,用于發送二進制的文件(即所有文件類型,如視頻、圖片、音樂、文檔都可以用此類型entype);還有一種enctype="text/plain"用于發送純文本內容。
2.表單請求方式必須為post。
3.接收時不能再用request.getParameter(),而是request.getInputStream()解析二進制流,得到ServletInputStream對象。
?
2.3重要方法
1.DiskFileItemFactory:
DiskFileItemFactory有兩個方法 :setSizeThreshold和setRepository
?setRepository方法用于設置當上傳文件尺寸大于setSizeThreshold方法設置的臨界值時,將文件以臨時文件形式保存在磁盤上的存放目錄。有一個對應的獲得臨時文件夾的 File getRespository() 方法。
? ? ? ? 注意:當從沒有調用此方法設置臨時文件存儲目錄時,默認采用系統默認的臨時文件路徑,可以通過系統屬性 java.io.tmpdir 獲取。如下代碼:
System.getProperty("java.io.tmpdir");
Tomcat系統默認臨時目錄為“<tomcat安裝目錄>/temp/”。
?setSizeThreshold方法說明:
??
?Apache文件上傳組件在解析上傳數據中的每個字段內容時,需要臨時保存解析出的數據,以便在后面進行數據的進一步處理(保存在磁盤特定位置或插入數據庫)。因為Java虛擬機默認可以使用的內存空間是有限的,超出限制時將會拋出“java.lang.OutOfMemoryError”錯誤。如果上傳的文件很大,例如800M的文件,在內存中將無法臨時保存該文件內容,Apache文件上傳組件轉而采用臨時文件來保存這些數據;但如果上傳的文件很小,例如600個字節的文件,顯然將其直接保存在內存中性能會更加好些。
? ? ? ?setSizeThreshold方法用于設置是否將上傳文件已臨時文件的形式保存在磁盤的臨界值(以字節為單位的int值),如果從沒有調用該方法設置此臨界值,將會采用系統默認值10KB。對應的getSizeThreshold() 方法用來獲取此臨界值。
2.FileItem:
? ? String getFieldName():獲取表單項的name的屬性值。
? ? String getName():獲取文件字段的文件名。如果是普通字段,則返回null
? ? String getString():獲取字段的內容。如果是普通字段,則是它的value值;如果是文件字段,則是文件內容。
? ? String getContentType():獲取上傳的文件類型,例如text/plain、image。如果是普通字段,則返回null。
? ? long getSize():獲取字段內容的大小,單位是字節。
? ? boolean isFormField():判斷是否是普通表單字段,若是,返回true,否則返回false。
? ? InputStream getInputStream():獲得文件內容的輸入流。如果是普通字段,則返回value值的輸入流。
2.4注意事項:
1.文件名中文亂碼處理:servletFileUpload.setHeaderEncoding("utf-8") 或 request.setCharacterEncoding("utf-8");
2.表單普通字段中文亂碼處理:new String(str.getBytes("iso-8859-1","utf-8"));
3.設置內存緩沖區的大小,默認為10KB:diskFileItemFactory.setSizeThreshold(1024*1024);
4.指定臨時文件目錄,如果單個文件的大小超過內存緩沖區,該文件將會臨時緩存在此目錄下diskFileItemFactory.setRepository(file);
5.設置單個文件大小限制,如果有某個文件超過此大小,將拋出FileUploadBase.FileSizeLimitExceededException:servletFileUpload.setFileSizeMax(1024*1024*10);
6.設置所有文件,也就是請求大小限制,如果文件總和超過此大小,將拋出FileUploadBase.SizeLimitExceededException:servletFileUpload.setSizeMax(1024*1024*20);
7.利用UUID生成偽隨機字符串作為文件名避免重復:UUID.randomUUID().toString();
8.將文件寫到硬盤上。寫完之后,系統會自動將放在臨時文件目錄的該文件刪除:fileItem.write(new File(path,fileName));
?
fileUpload用來上傳文件,主要任務解析request:
parseRequest(request)?
總結
以上是生活随笔為你收集整理的SpingMVC框架:fileUpload组件原理和实现的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 2021-11-06深度学习
- 下一篇: 量子计算机首次成功模拟化学反应