SprngMVC 學(xué)習(xí)(六)文件上傳

Spring MVC同樣支持文件上傳功能及刻,不過該功能默認(rèn)未開啟,因?yàn)榭赡苡行╅_發(fā)者可能希望自己處理文件上傳過程咐吼。

Spring的文件上傳功能在org.springframework.web.multipart包下,有兩個(gè)MultipartResolver實(shí)現(xiàn)用來(lái)支持文件上傳功能:

  • 一個(gè)是基于Commons FileUpload
  • 另一個(gè)基于Servlet 3.0 multipart請(qǐng)求解析功能

這兩個(gè)MultipartResolver差不多,一個(gè)需要添加Commons FileUpload的依賴冈止,另一個(gè)需要在Servlet 3.0容器上運(yùn)行。大家可以根據(jù)需要選擇候齿。

如果想要使用Spring的文件上傳功能熙暴,需要在文件上下文中配置MultipartResolver。

定義MultipartResolver

使用Commons FileUpload MultipartResolver

在配置文件中添加如下一段慌盯,我們可以在Bean定義中配置上傳文件大小等屬性周霉。

<!-- 文件上傳 -->
    <bean id="multipartResolver"
        class="org.springframework.web.multipart.commons.CommonsMultipartResolver">

        <!-- 設(shè)置上傳文件的最大尺寸位30M -->
        <property name="maxUploadSize" value="31457280" />
        <property name="defaultEncoding" value="UTF-8" />
        <!-- 是否延遲加載,在需要的時(shí)候才進(jìn)行上傳文件的解析 -->
        <property name="resolveLazily" value="true" />
        <!-- 文件上傳的臨時(shí)路徑亚皂,文件上傳完成后俱箱,臨時(shí)目錄中的臨時(shí)文件會(huì)被自動(dòng)清除 -->
        <property name="uploadTempDir" value="upload/temp" />
    </bean>

defaultEncoding必須和JSP的pageEncoding屬性一致,以便正確讀取表單的內(nèi)容灭必。

注意:引入commons-fileupload.jar狞谱、commons-io.jar 兩個(gè)包。


使用Servlet 3.0 MultipartResolver

由于使用的是Servlet API提供的文件上傳功能禁漓,所以文件大小等配置需要在web.xml中進(jìn)行配置跟衅。我們需要在dispathcer-servlet中添加<multipart-config>標(biāo)簽,它有四個(gè)子標(biāo)簽來(lái)設(shè)置文件上傳的屬性播歼。

這四個(gè)屬性如下:

  • location 伶跷,臨時(shí)文件的存放位置,這個(gè)路徑必須是絕對(duì)路徑荚恶。
  • fileSizeThreshold撩穿,文件起始值,大于該值文件才會(huì)被臨時(shí)保存谒撼,單位是字節(jié)食寡。
  • MaxFileSize,單個(gè)文件的最大值廓潜,單位是字節(jié)抵皱,不管上傳幾個(gè)文件,只要有一個(gè)文件大小超過該值就會(huì)拋出IllegalStateException辩蛋。
  • maxRequestSize呻畸,文件上傳請(qǐng)求的最大值,單位是字節(jié)悼院,主要作用是當(dāng)上傳多個(gè)文件是配置整個(gè)請(qǐng)求的大小伤为,當(dāng)超出該值是拋出IllegalStateException。
<servlet>
    <servlet-name>dispatcher</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
    <async-supported>true</async-supported>
    <multipart-config>
        <max-file-size>100000</max-file-size>
    </multipart-config>
</servlet>

然后我們?cè)赟pring配置文件中添加Servlet 3.0 MultipartResolver。

<bean id="multipartResolver"
        class="org.springframework.web.multipart.support.StandardServletMultipartResolver">
</bean>

上傳文件

配置好了Multipart解析器之后绞愚,我們就可以接收文件了叙甸。

首先定義一個(gè)頁(yè)面fileupload.jsp,用于上傳文件并顯示服務(wù)器中的文件位衩。

注意:在表單中我們必須添加<code>enctype="multipart/form-data"</code>才能正確的上傳文件裆蒸。

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>文件上傳</title>
    <meta charset="utf-8">

</head>
<body>
<h2>文件上傳</h2>
<form action="<c:url value="/mvc/uploadfile.html"/>"
      method="post" enctype="multipart/form-data">
    <label for="file">文件</label>
    <input type="file" name="file" id="file"/>
    <br>
    <input type="submit" value="提交">${result}
</form>

</body>
</html>

然后就可以在控制器中獲取文件了。由于MultipartFile和它對(duì)應(yīng)的臨時(shí)文件會(huì)在方法結(jié)束之后被Spring清除糖驴,所以我們必須在方法中將文件保存到合適的地方僚祷。

在請(qǐng)求方法中,我們可以像普通參數(shù)那樣獲取上傳的文件贮缕,只不過文件對(duì)應(yīng)的類型是MultipartFile辙谜,如果使用的是Servlet 3.0標(biāo)準(zhǔn)的,那么類型還可以是javax.servlet.http.Part跷睦。

下面寫了兩個(gè)處理方法筷弦,第一個(gè)將MultipartFile轉(zhuǎn)化為File保存在web目錄下的upload文件夾中肋演,第二個(gè)方法用于獲取保存在該文件夾的文件抑诸。

    @RequestMapping("/uploadfile.html")
    public String uploadFile(@RequestParam("file") MultipartFile multipartFile) throws IOException {
        
        // 獲取到web的根目錄
        String path = System.getProperty("tansungWeb.root");
        
        if (!multipartFile.isEmpty()) {
            // 獲取到源文件名
            String filename = multipartFile.getOriginalFilename();
            // 獲取文件的后綴名
            String suffix = filename.substring(filename.lastIndexOf(".") + 1).toLowerCase();
            // 如果后綴為mp3的,一上傳文件原名保存爹殊,否則以時(shí)間戳文文件名進(jìn)行保存
            if (!suffix.equals("mp3")) {
                //FileUtils.copyInputStreamToFile(multipartFile.getInputStream(),new File(path + "http://upload//", System.currentTimeMillis() + "." + suffix));
                multipartFile.transferTo(new File(path + "http://upload//", System.currentTimeMillis() + "." + suffix));
            } else {
                FileUtils.copyInputStreamToFile(multipartFile.getInputStream(), new File(path + "http://upload//", filename));
            }
        }
        model.addAttribute("result", "上傳成功蜕乡!");
        return "fileupload";
    }
}

Spring MVC會(huì)將上傳文件綁定到MultipartFile對(duì)象中。MultipartFile提供了獲取上傳文件內(nèi)容梗夸、文件名等內(nèi)容层玲,通過其transferTo()方法還可將文件存儲(chǔ)到硬件中。

// 獲取文件數(shù)據(jù)
byte[] getBytes();
// 獲取文件MIME類型反症,如image/jprg等
String getContentType();
// 獲取文件流
InputStream getInputStream();
// 獲取表單中文件組件的名字
String getName();
// 獲取上傳文件的原名
String getOriginalFilename();
// 獲取文件的字節(jié)大小辛块,單位為byte
long getSize();
// 是否有上傳的文件
boolean isEmpty();
// 可以使用該文件將上傳文件保存到一個(gè)目標(biāo)文件中
void transferTo(File dest);

將上傳的文件列出來(lái),實(shí)現(xiàn)下載的功能铅碍。

    @RequestMapping("/listfile")
    public String listFile(Model model)
            throws IOException, ServletException {
        // 獲取上傳文件的目錄
        String uploadFilePath = System.getProperty("tansungWeb.root") + "upload";
        // 存儲(chǔ)要下載的文件名
        Map<String, String> fileNameMap = new HashMap<String, String>();
        // 遞歸遍歷filepath目錄下的所有文件和目錄润绵,將文件的文件名存儲(chǔ)到map集合中
        // File既可以代表一個(gè)文件也可以代表一個(gè)目錄
        listfile(new File(uploadFilePath), fileNameMap);
        // 將Map集合發(fā)送到list.jsp頁(yè)面進(jìn)行顯示
        model.addAttribute("fileNameMap", fileNameMap);
        
        return "list";
    }

    /**
     * @Method: listfile
     * @Description: 遞歸遍歷指定目錄下的所有文件
     * @param file
     *            即代表一個(gè)文件,也代表一個(gè)文件目錄
     * @param map
     *            存儲(chǔ)文件名的Map集合
     */
    public void listfile(File file, Map<String, String> map) {
        // 如果file代表的不是一個(gè)文件胞谈,而是一個(gè)目錄
        if (!file.isFile()) {
            // 列出該目錄下的所有文件和目錄
            File files[] = file.listFiles();
            // 遍歷files[]數(shù)組
            for (File f : files) {
                // 遞歸
                listfile(f, map);
            }
        } else {
            /*
             * 處理文件名尘盼,上傳后的文件是以u(píng)uid_文件名的形式去重新命名的,去除文件名的uuid_部分
             * file.getName().indexOf("_")檢索字符串中第一次出現(xiàn)"_"字符的位置烦绳,如果文件名類似于:
             * 9349249849-88343-8344_阿凡達(dá).avi
             * 那么file.getName().substring(file.getName().indexOf("_")+1)
             * 處理之后就可以得到阿凡達(dá).avi部分
             */
            String realName = file.getName().substring(file.getName().indexOf("_") + 1);
            // file.getName()得到的是文件的原始名稱卿捎,這個(gè)名稱是唯一的,因此可以作為key径密,realName是處理過后的名稱午阵,有可能會(huì)重復(fù)
            map.put(file.getName(), realName);
        }
    }

編寫list.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>  
<%
    String path = request.getContextPath();
    String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort()
            + path + "/";
%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>下載列表顯示頁(yè)面</title>
</head>
<body>
    <!-- 遍歷Map集合 -->  
    <c:forEach var="files" items="${fileNameMap}"> 
        
        <c:url value="/mvc/download" var="downurl">
            <c:param name="filename" value="${files.key}"></c:param>
        </c:url> 
        ${files.value}<a href="${downurl}">下載</a>  
         <!-- 
        <a href="<c:url value="/mvc/download?filename=${files.value}"/>">${files.value}</a>
         -->
        <br/>  
    </c:forEach>

</body>
</html>

下載文件

下載功能的實(shí)現(xiàn)

@RequestMapping("/download")
public void downloadFile(@RequestParam("filename") String filename, Model model, HttpServletRequest req, HttpServletResponse resp)
        throws IOException {

    filename = new String(filename.getBytes("iso8859-1"), "UTF-8");
    //上傳的文件都是保存在/WEB-INF/upload目錄下的子目錄當(dāng)中
    String fileSaveRootPath = System.getProperty("tansungWeb.root") + "upload";
    // 得到要下載的文件
    File file = new File(fileSaveRootPath + "\\" + filename);
    
    // 如果文件不存在
    if (!file.exists()) {
        model.addAttribute("message", "您要下載的資源已被刪除!享扔!");
    }
    // 處理文件名
    String realname = filename.substring(filename.indexOf("_") + 1);
    // 設(shè)置響應(yīng)頭底桂,控制瀏覽器下載該文件
    resp.setHeader("content-disposition", "attachment;filename=" + URLEncoder.encode(realname, "UTF-8"));
    // 讀取要下載的文件括细,保存到文件輸入流
    FileInputStream in = new FileInputStream(fileSaveRootPath + "\\" + filename);
    // 創(chuàng)建輸出流
    OutputStream out = resp.getOutputStream();
    // 創(chuàng)建緩沖區(qū)
    byte buffer[] = new byte[1024];
    int len = 0;
    // 循環(huán)將輸入流中的內(nèi)容讀取到緩沖區(qū)當(dāng)中
    while ((len = in.read(buffer)) > 0) {
        // 輸出緩沖區(qū)的內(nèi)容到瀏覽器,實(shí)現(xiàn)文件下載
        out.write(buffer, 0, len);
    }
    // 關(guān)閉文件輸入流
    in.close();
    // 關(guān)閉輸出流
    out.close();
    
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末戚啥,一起剝皮案震驚了整個(gè)濱河市奋单,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌猫十,老刑警劉巖览濒,帶你破解...
    沈念sama閱讀 217,406評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異拖云,居然都是意外死亡贷笛,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門宙项,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)乏苦,“玉大人,你說(shuō)我怎么就攤上這事尤筐』慵觯” “怎么了?”我有些...
    開封第一講書人閱讀 163,711評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵盆繁,是天一觀的道長(zhǎng)掀淘。 經(jīng)常有香客問我,道長(zhǎng)油昂,這世上最難降的妖魔是什么革娄? 我笑而不...
    開封第一講書人閱讀 58,380評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮冕碟,結(jié)果婚禮上拦惋,老公的妹妹穿的比我還像新娘。我一直安慰自己安寺,他們只是感情好厕妖,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,432評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著我衬,像睡著了一般叹放。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上挠羔,一...
    開封第一講書人閱讀 51,301評(píng)論 1 301
  • 那天井仰,我揣著相機(jī)與錄音,去河邊找鬼破加。 笑死俱恶,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播合是,決...
    沈念sama閱讀 40,145評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼了罪,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了聪全?” 一聲冷哼從身側(cè)響起泊藕,我...
    開封第一講書人閱讀 39,008評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎难礼,沒想到半個(gè)月后娃圆,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,443評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡蛾茉,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,649評(píng)論 3 334
  • 正文 我和宋清朗相戀三年讼呢,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片谦炬。...
    茶點(diǎn)故事閱讀 39,795評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡悦屏,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出键思,到底是詐尸還是另有隱情础爬,我是刑警寧澤,帶...
    沈念sama閱讀 35,501評(píng)論 5 345
  • 正文 年R本政府宣布稚机,位于F島的核電站幕帆,受9級(jí)特大地震影響获搏,放射性物質(zhì)發(fā)生泄漏赖条。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,119評(píng)論 3 328
  • 文/蒙蒙 一常熙、第九天 我趴在偏房一處隱蔽的房頂上張望纬乍。 院中可真熱鬧,春花似錦裸卫、人聲如沸仿贬。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)茧泪。三九已至,卻和暖如春聋袋,著一層夾襖步出監(jiān)牢的瞬間队伟,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工幽勒, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留嗜侮,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,899評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像锈颗,于是被迫代替她去往敵國(guó)和親顷霹。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,724評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容