在開發過程中文件的上傳下載很常用。這裡簡單的總結一下:
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"> <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; }以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持武林網。