在開發過程中檔案的上傳下載很常用。這裡簡單的總結一下:
1.檔案上傳必須滿足的條件:
a、 頁面表單的method必須是post 因為get傳送的資料太小了
b、 頁面表單的enctype必須是multipart/form-data類型的
c、 表單中提供上傳輸入欄位
代碼細節: 用戶端表單中:<form enctype="multipart/form-data"/>
(如果沒有這個屬性,則服務端讀取的檔案路徑會因為瀏覽器的不同而不同)
服務端ServletInputStream is=request.getInputStream();用流的方式擷取請求本文內容,進一步的解析。
2.上傳檔案的細節:
(1)為什麼設定表單類型為:multipart/form-data.是設定這個表單傳遞的不是key=value值。傳遞的是位元組碼.
表單與請求的對應關係:
如上可以看出在設定表單類型為:multipart/form-data之後,在HTTP請求體中將你選擇的檔案初始化為二進位,如上圖中的Cookie之下的一串的隨機字串下的內容。
但注意,在標識檔案(即一串隨機字串)所分割出來的檔案位元組碼中有兩行特殊字元,即第一行內容檔案頭和一行空行。之後的第三行才是二進位的檔案內容。
所以在服務端接受用戶端上傳的檔案時,擷取HTTP請求參數中的檔案二進位時,要去掉前三行。
3.自己手工解析上傳的txt檔案:
import java.io.BufferedReader;import java.io.IOException;import java.io.InputStream;import java.io.InputStreamReader;import java.io.PrintWriter;import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;/** * 如果一個表單的類型是post且enctype為multipart/form-date * 則所有資料都是以二進位的方式向伺服器上傳遞。 * 所以req.getParameter("xxx")永遠為null。 * 只可以通過req.getInputStream()來擷取資料,擷取本文的資料 * * @author wangxi * */public class UpServlet extends HttpServlet { public void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setCharacterEncoding("UTF-8"); String txt = req.getParameter("txt");//返回的是null System.err.println("txt is :"+txt); System.err.println("========================================="); InputStream in = req.getInputStream();// byte[] b= new byte[1024];// int len = 0;// while((len=in.read(b))!=-1){// String s = new String(b,0,len);// System.err.print(s);// } BufferedReader br = new BufferedReader(new InputStreamReader(in)); String firstLine = br.readLine();//讀取第一行,且第一行是分隔字元號,即隨機字串 String fileName = br.readLine();//第二行檔案資訊,從中截取出檔案名稱 fileName = fileName.substring(fileName.lastIndexOf("\\")+1);// xxxx.txt" fileName = fileName.substring(0,fileName.length()-1); br.readLine(); br.readLine(); String data = null; //擷取當前項目的運行路徑 String projectPath = getServletContext().getRealPath("/up"); PrintWriter out = new PrintWriter(projectPath+"/"+fileName); while((data=br.readLine())!=null){ if(data.equals(firstLine+"--")){ break; } out.println(data); } out.close(); }}
4.使用apache-fileupload處理檔案上傳:
架構:是指將使用者經常處理的業務進行一個代碼封裝。讓使用者可以方便的調用。
目前檔案上傳的(架構)組件:
Apache—-fileupload -
Orialiy – COS – 2008() -
Jsp-smart-upload – 200M。
用fileupload上傳檔案:
需要匯入第三方包:
Apache-fileupload.jar – 檔案上傳核心包。
Apache-commons-io.jar – 這個包是fileupload的依賴包。同時又是一個工具包。
核心類:
DiskFileItemFactory – 設定磁碟空間,儲存臨時檔案。只是一個具類。
ServletFileUpload - 檔案上傳的核心類,此類接收request,並解析reqeust。
ServletfileUpload.parseRequest(requdest) - List<FileItem>
注:一個FileItem就是一個標識的開始:---------243243242342 到 ------------------245243523452—就是一個FileItem
第一步:匯入包:
第二步:書寫一個servlet完成doPost方法
/** * DiskFileItemFactory構造的兩個參數 * 第一個參數:sizeThreadHold - 設定緩衝(記憶體)儲存多少位元組資料,預設為10K * 如果一個檔案沒有大於10K,則直接使用記憶體直接儲存成檔案就可以了。 * 如果一個檔案大於10K,就需要將檔案先儲存到臨時目錄中去。 * 第二個參數 File 是指臨時目錄位置 * */public class Up2Servlet extends HttpServlet { public void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setCharacterEncoding("UTf-8"); //擷取項目的路徑 String path = getServletContext().getRealPath("/up"); //第一步聲明diskfileitemfactory工廠類,用於在指的磁碟上設定一個臨時目錄 DiskFileItemFactory disk = new DiskFileItemFactory(1024*10,new File("/home/wang/")); //第二步:聲明ServletFileUpoload,接收上面的臨時目錄 ServletFileUpload up = new ServletFileUpload(disk); //第三步:解析request try { List<FileItem> list = up.parseRequest(req); //如果就一個檔案 FileItem file = list.get(0); //擷取檔案名稱,帶路徑 String fileName = file.getName(); fileName = fileName.substring(fileName.lastIndexOf("\\")+1); //擷取檔案的類型 String fileType = file.getContentType(); //擷取檔案的位元組碼 InputStream in = file.getInputStream(); //聲明輸出位元組流 OutputStream out = new FileOutputStream(path+"/"+fileName); //檔案copy byte[] b = new byte[1024]; int len = 0; while((len=in.read(b))!=-1){ out.write(b,0,len); } out.close(); long size = file.getInputStream().available(); //刪除上傳的臨時檔案 file.delete(); //顯示資料 resp.setContentType("text/html;charset=UTf-8"); PrintWriter op = resp.getWriter(); op.print("檔案上傳成功<br/>檔案名稱:"+fileName); op.print("<br/>檔案類型:"+fileType); op.print("<br/>檔案大小(bytes)"+size); } catch (Exception e) { e.printStackTrace(); } }}
5.使用該架構上傳多個檔案:
第一步:修改頁面的表單為多個input type=”file”
<form action="<c:url value='/Up3Servlet'/>" method="post" enctype="multipart/form-data"> File1:<input type="file" name="txt"><br/> File2:<input type="file" name="txt"><br/> <input type="submit"/> </form>
第二步:遍曆list
public class Up3Servlet extends HttpServlet { public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("UTF-8"); String path = getServletContext().getRealPath("/up"); //聲明disk DiskFileItemFactory disk = new DiskFileItemFactory(); disk.setSizeThreshold(1024*1024); disk.setRepository(new File("d:/a")); //聲明解析requst的servlet ServletFileUpload up = new ServletFileUpload(disk); try{ //解析requst List<FileItem> list = up.parseRequest(request); //聲明一個list<map>封裝上傳的檔案的資料 List<Map<String,String>> ups = new ArrayList<Map<String,String>>(); for(FileItem file:list){ Map<String,String> mm = new HashMap<String, String>(); //擷取檔案名稱 String fileName = file.getName(); fileName = fileName.substring(fileName.lastIndexOf("\\")+1); String fileType = file.getContentType(); InputStream in = file.getInputStream(); int size = in.available(); //使用工具類 FileUtils.copyInputStreamToFile(in,new File(path+"/"+fileName)); mm.put("fileName",fileName); mm.put("fileType",fileType); mm.put("size",""+size); ups.add(mm); file.delete(); } request.setAttribute("ups",ups); //轉寄 request.getRequestDispatcher("/jsps/show.jsp").forward(request, response); }catch(Exception e){ e.printStackTrace(); } }}
如上就是上傳檔案的常用做法。現在我們在來看看fileupload的其他查用API.
判斷一個fileItem是否是file(type=file)對象或是text(type=text|checkbox|radio)對象:
boolean isFormField() 如果是text|checkbox|radio|select這個值就是true.
6.處理帶說明資訊的圖片
public class UpDescServlet extends HttpServlet { public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("UTF-8");//可以擷取中文的檔案名稱 String path = getServletContext().getRealPath("/up"); DiskFileItemFactory disk = new DiskFileItemFactory(); disk.setRepository(new File("d:/a")); try{ ServletFileUpload up = new ServletFileUpload(disk); List<FileItem> list = up.parseRequest(request); for(FileItem file:list){ //第一步:判斷是否是普通的表單項 if(file.isFormField()){ String fileName = file.getFieldName();//<input type="text" name="desc">=desc String value = file.getString("UTF-8");//預設以ISO方式讀取資料 System.err.println(fileName+"="+value); }else{//說明是一個檔案 String fileName = file.getName(); fileName = fileName.substring(fileName.lastIndexOf("\\")+1); file.write(new File(path+"/"+fileName)); System.err.println("檔案名稱是:"+fileName); System.err.println("檔案大小是:"+file.getSize()); file.delete(); } } }catch(Exception e){ e.printStackTrace(); } }}
7.檔案上傳的效能提升
在解析request擷取FileItem的集合的時候,使用:
FileItemIterator it= up.getItemIterator(request);
比使用
List<FileItem> list = up.parseRequest(request);
效能上要好的多。
範例程式碼:
public class FastServlet extends HttpServlet { public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("UTF-8"); String path = getServletContext().getRealPath("/up"); DiskFileItemFactory disk = new DiskFileItemFactory(); disk.setRepository(new File("d:/a")); try{ ServletFileUpload up = new ServletFileUpload(disk); //以下是迭代器模式 FileItemIterator it= up.getItemIterator(request); while(it.hasNext()){ FileItemStream item = it.next(); String fileName = item.getName(); fileName=fileName.substring(fileName.lastIndexOf("\\")+1); InputStream in = item.openStream(); FileUtils.copyInputStreamToFile(in,new File(path+"/"+fileName)); } }catch(Exception e){ e.printStackTrace(); } }}
8.檔案的下載
既可以是get也可以是post。
public void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setCharacterEncoding("UTF-8"); String name = req.getParameter("name"); //第一步:設定響應的類型 resp.setContentType("application/force-download"); //第二讀取檔案 String path = getServletContext().getRealPath("/up/"+name); InputStream in = new FileInputStream(path); //設定回應標頭 //對檔案名稱進行url編碼 name = URLEncoder.encode(name, "UTF-8"); resp.setHeader("Content-Disposition","attachment;filename="+name); resp.setContentLength(in.available()); //第三步:開始檔案copy OutputStream out = resp.getOutputStream(); byte[] b = new byte[1024]; int len = 0; while((len=in.read(b))!=-1){ out.write(b,0,len); } out.close(); in.close(); }在使用J2EE流行架構時
使用架構內部封裝好的來完成上傳下載更為簡單:
Struts2完成上傳.
在使用Struts2進行開發時,匯入的jar包不難發現存在 commons-fileupload-1.3.1.jar 包。通過上面的學習我們已經可以使用它進行檔案的上傳下載了。但Struts2在進行了進一步的封裝。
view
<form action="fileUpload.action" method="post" enctype="multipart/form-data"> username: <input type="text" name="username"><br> file: <input type="file" name="file"><br> <input type="submit" value="submit"> </form>
Controller
public class FileUploadAction extends ActionSupport{ private String username; //注意,file並不是指前端jsp上傳過來的檔案本身,而是檔案上傳過來存放在臨時檔案夾下面的檔案 private File file; //提交過來的file的名字 //struts會自動截取上次檔案的名字注入給該屬性 private String fileFileName; //getter和setter此時為了節約篇幅省掉 @Override public String execute() throws Exception { //儲存上傳檔案的路徑 String root = ServletActionContext.getServletContext().getRealPath("/upload"); //擷取臨時檔案輸入資料流 InputStream is = new FileInputStream(file); //輸出檔案 OutputStream os = new FileOutputStream(new File(root, fileFileName)); //列印出上傳的檔案的檔案名稱 System.out.println("fileFileName: " + fileFileName); // 因為file是存放在臨時檔案夾的檔案,我們可以將其檔案名稱和檔案路徑列印出來,看和之前的fileFileName是否相同 System.out.println("file: " + file.getName()); System.out.println("file: " + file.getPath()); byte[] buffer = new byte[1024]; int length = 0; while(-1 != (length = is.read(buffer, 0, buffer.length))) { os.write(buffer); } os.close(); is.close(); return SUCCESS; }}
首先我們要清楚一點,這裡的file並不是真正指代jsp上傳過來的檔案,當檔案上傳過來時,struts2首先會尋找struts.multipart.saveDir(這個是在default.properties裡面有)這個name所指定的存放位置(預設是空),我們可以在我們項目的struts2中來指定這個臨時檔案存放位置。
<constant name="struts.multipart.saveDir" value="/repository"/>
如果沒有設定struts.multipart.saveDir,那麼將預設使用javax.servlet.context.tempdir指定的地址,javax.servlet.context.tempdir的值是由伺服器來確定的,例如:假如我的web工程的context是abc,伺服器使用Tomcat,那麼savePath就應該是%TOMCAT_HOME%/work/Catalina/localhost/abc_,臨時檔案的名稱類似於upload__1a156008_1373a8615dd__8000_00000001.tmp,每次上傳的臨時檔案名稱可能不同,但是大致是這種樣式。而且如果是使用Eclipse中的Servers裡面配置Tomcat並啟動的話,那麼上面地址中的%TOMCAT_HOME%將不會是系統中的實際Tomcat根目錄,而會是Eclipse給它指定的地址,例如我本地的地址是這樣的:/home/wang/EclipseJavaCode/.metadata/.plugins/org.eclipse.wst.server.core/tmp0/work/Catalina/localhost/abc/upload__1a156008_1373a8615dd__8000_00000001.tmp。
Struts2完成下載.
struts2的檔案下載更簡單,就是定義一個輸入資料流,然後將檔案寫到輸入資料流裡面就行,關鍵配置還是在struts.xml這個設定檔裡配置:
public class FileDownloadAction extends ActionSupport{ //要下載檔案在伺服器上的路徑 private String path; //要下載檔案的檔案名稱 private String downloadFileName; //寫入getter和setter public InputStream getDownloadFile() { return ServletActionContext.getServletContext().getResourceAsStream(path); } @Override public String execute() throws Exception { //當前action預設在valuestack的棧頂 setDownloadFileName(xxx); return SUCCESS; }}
action只是定義了一個輸入資料流downloadFile,然後為其提供getter方法就行,接下來我們看看struts.xml的設定檔:
<action name="fileDownload" class="com.struts2.FileDownloadAction"> <result name="download" type="stream"> <param name="contentDisposition">attachment;fileName="${downloadFileName}"</param> <param name="inputName">downloadFile</param> </result> </action>
struts.xml設定檔有幾個地方我們要注意,首先是result的類型,type一定要定義成stream類型_,告訴action這是檔案下載的result,result元素裡面一般還有param子項目,這個是用來設定檔案下載時的參數,inputName這個屬性就是得到action中的檔案輸入資料流,名字一定要和action中的輸入資料流屬性名稱字相同,然後就是contentDisposition屬性,這個屬性一般用來指定我們希望通過怎麼樣的方式來處理下載的檔案,如果值是attachment,則會彈出一個下載框,讓使用者選擇是否下載,如果不設定這個值,那麼瀏覽器會首先查看自己能否開啟下載的檔案,如果能,就會直接開啟所下載的檔案,(這當然不是我們所需要的),另外一個值就是filename這個就是檔案在下載時所提示的檔案下載名字。在配置完這些資訊後,我們就能過實現檔案的下載功能了。
SpringMVC完成上傳:
view於struts2樣本中的完全一樣。此出不在寫出。
Controller:
@Controller@RequestMapping(value="fileOperate")public class FileOperateAction { @RequestMapping(value="upload") public String upload(HttpServletRequest request,@RequestParam("file") MultipartFile photoFile){ //上傳檔案儲存的路徑 String dir = request.getSession().getServletContext().getRealPath("/")+"upload"; //原始的檔案名稱 String fileName = photoFile.getOriginalFilename(); //擷取副檔名 String extName = fileName.substring(fileName.lastIndexOf(".")); //防止檔案名稱衝突,把名字小小修改一下 fileName = fileName.substring(0,fileName.lastIndexOf(".")) + System.nanoTime() + extName; FileUtils.writeByteArrayToFile(new File(dir,fileName),photoFile.getBytes()); return "success"; } }
SpringMVC完成下載:
@RequestMapping("/download") public String download(String fileName, HttpServletRequest request, HttpServletResponse response) { response.setCharacterEncoding("utf-8"); response.setContentType("multipart/form-data"); response.setHeader("Content-Disposition", "attachment;fileName=" + fileName); try { InputStream inputStream = new FileInputStream(new File(檔案的路徑); OutputStream os = response.getOutputStream(); byte[] b = new byte[2048]; int length; while ((length = inputStream.read(b)) > 0) { os.write(b, 0, length); } // 這裡主要關閉。 os.close(); inputStream.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } // 傳回值要注意,要不然就出現下面這句錯誤! //java+getOutputStream() has already been called for this response return null; }
以上就是本文的全部內容,希望對大家的學習有所協助,也希望大家多多支援雲棲社區。