最近的專案中涉及到檔案的上傳跟下載的問題,就自己所涉及到的方面做出如下表述。
首先是檔案上傳部分,專案的要求是通用性較好,所以只需要傳入目標路徑即可。引數的傳遞通過Form表單傳值,在目標路徑下新建一個File型別的檔案,然後通過流的方式將需要上傳的檔案寫入新建的檔案中。此方法適用於web開發過程中上傳文件類的檔案,如果你檔案過大請研究ftp相關的知識,筆者所接觸的ftp傳檔案限於C#中,這裡不做表述。具體程式碼如下:
1 public void fileUpload(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException 2 { 3 String filePath = new String(); 4 request.setCharacterEncoding("UTF-8"); 5 response.setContentType("text/html; charset=GB2312"); 6 try 7 { 8 DiskFileItemFactory factory = new DiskFileItemFactory(); 9 //設定快取中最大允許快取為2M 10 factory.setSizeThreshold(2 * 1024 * 1024); 11 ServletFileUpload upload = new ServletFileUpload(factory); 12 //解決中文檔名為亂碼的問題 13 upload.setHeaderEncoding("UTF-8"); 14 List<FileItem> fileList = upload.parseRequest(request); 15 Iterator iter = fileList.iterator(); 16 String newFileName = ""; 17 while (iter.hasNext()) 18 { 19 //獲取檔案 20 FileItem fileItem = (FileItem)iter.next(); 21 //獲取檔案上傳的路徑 22 String typeName = fileItem.getFieldName(); 23 if(("destPath").equals(typeName)) 24 { 25 filePath = fileItem.getString("utf-8"); 26 } 27 if(("filename").equals(typeName)) 28 { 29 newFileName = fileItem.getString("utf-8"); 30 } 31 String fileName = new String(); 32 if (!fileItem.isFormField()) 33 { 34 String name = fileItem.getName(); 35 if(StringUtil.isNullOrEmpty(name)) 36 { 37 continue; 38 } 39 fileName = name; 40 fileName = fileName.substring(fileName.lastIndexOf("\\") + 1); 41 File file = new File(filePath); 42 if(!file.exists()) 43 { 44 file.mkdirs(); 45 } 46 //向指定的路徑寫檔案 47 if(newFileName.length()>0) 48 { 49 fileName = newFileName; 50 } 51 fileItem.write(new File(filePath, fileName)); 52 } 53 } 54 } 55 catch (Exception ex) 56 { 57 throw new ServletException("上傳檔案失敗!", ex); 58 } 59 }
第二部分是關於檔案批量下載的文件。根據客戶的觀點有上傳就要下載的需求,一開始也做了相關的開發,結果發現下載時的檔案全部存在伺服器端,跟需求不一致。仔細推敲了下程式碼原來自己寫的有問題,在客戶端是選擇檔案的儲存路徑,然後將檔案下載,其實只是實現了本地的遷移,並沒有在把檔案下載到客戶端。後來的想法是把檔案打包下載,用FileOutputStream,實現下載的問題,可是如果下載的檔案中含有中文字元,就會出現亂碼。不得已百度了下,原來是編碼的問題,使用java自帶的檔案輸出類不能解決壓縮檔案中檔名亂碼的問題。解決方法:使用ant.jar包,建立壓縮檔案時,可以設定檔案的編碼格式,檔名亂碼的問題就解決了。由於筆者在開發過程中資料庫中有檔案的路徑,所以下列檔案的檔案路徑的獲取是查詢得到。
具體的解決程式碼如下:
引用ant.jar包中的FileOutputStream類
import org.apache.tools.zip.ZipEntry; import org.apache.tools.zip.ZipOutputStream;
打包下載多個檔案的思路就是在服務端建立一個新的壓縮檔案(zip格式),然後將下載的多個檔案寫入該壓縮包中,然後以流的形式輸出壓縮檔案寫到客戶端,實現下載功能。
1 public void downloadFiles(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException 2 { 3 request.setCharacterEncoding("UTF-8"); 4 String docId = new String(request.getParameter("docId").getBytes("ISO-8859-1"), "UTF-8"); 5 String downloadType = new String(request.getParameter("downloadType").getBytes("ISO-8859-1"), "UTF-8"); 6 7 Document doc = null; 8 try 9 { 10 doc = getFilePath(docId); //此方法獲取檔案的路徑,這個不顯示 11 } 12 catch (SQLException ex) 13 { 14 ex.printStackTrace(); 15 } 16 17 List<Element> elements = doc.getRootElement().elements(); 18 List<File> files = new ArrayList<File>(); 19 int index = 0; 20 long fileLength = 0; 21 for(Element ele : elements) 22 { 23 String filePath = FormatUtil.tryGetItemText(ele, "FILE_PATH", "") + FormatUtil.tryGetItemText(ele, "FILE_NAME", ""); 24 File file = new File(filePath); 25 files.add(file); 26 fileLength += file.length(); 27 index++; 28 } 29 String fileName = UUID.randomUUID().toString() + ".zip"; 30 //在伺服器端建立打包下載的臨時檔案 31 String outFilePath = "C:\\" + fileName; 32 File file = new File(outFilePath); 33 //檔案輸出流 34 FileOutputStream outStream = new FileOutputStream(file); 35 //壓縮流 36 ZipOutputStream toClient = new ZipOutputStream(outStream); 37 toClient.setEncoding("gbk"); 38 zipFile(files, toClient); 39 toClient.close(); 40 outStream.close(); 41 this.downloadZip(file, response); 42 } 43 /** 44 * 壓縮檔案列表中的檔案 45 * @param files 46 * @param outputStream 47 * @throws IOException 48 */ 49 public static void zipFile(List files, ZipOutputStream outputStream) throws IOException,ServletException 50 { 51 try 52 { 53 int size = files.size(); 54 //壓縮列表中的檔案 55 for(int i = 0; i < size; i++) 56 { 57 File file = (File) files.get(i); 58 zipFile(file, outputStream); 59 } 60 } 61 catch(IOException e) 62 { 63 throw e; 64 } 65 } 66 /** 67 * 將檔案寫入到zip檔案中 68 * @param inputFile 69 * @param outputstream 70 * @throws Exception 71 */ 72 public static void zipFile(File inputFile, ZipOutputStream outputstream) throws IOException,ServletException 73 { 74 try{ 75 if(inputFile.exists()) 76 { 77 if(inputFile.isFile()) 78 { 79 FileInputStream inStream = new FileInputStream(inputFile); 80 BufferedInputStream bInStream = new BufferedInputStream(inStream); 81 ZipEntry entry = new ZipEntry(inputFile.getName()); 82 outputstream.putNextEntry(entry); 83 84 final int MAX_BYTE = 10 * 1024 *1024; //最大的流為10M 85 long streamTotal = 0; //接受流的容量 86 int streamNum = 0; //流需要分開的數量 87 int leaveByte = 0; //檔案剩下的字元數 88 byte[] inOutbyte; //byte陣列接受檔案的資料 89 90 streamTotal = bInStream.available(); //通過available方法取得流的最大字元數 91 streamNum = (int)Math.floor(streamTotal / MAX_BYTE); //取得流檔案需要分開的數量 92 leaveByte = (int)streamTotal % MAX_BYTE; //分開檔案之後,剩餘的數量 93 94 if (streamNum > 0) 95 { 96 for(int j = 0; j < streamNum; ++j) 97 { 98 inOutbyte = new byte[MAX_BYTE]; 99 //讀入流,儲存在byte陣列 100 bInStream.read(inOutbyte, 0, MAX_BYTE); 101 outputstream.write(inOutbyte, 0, MAX_BYTE); //寫出流 102 } 103 } 104 //寫出剩下的流資料 105 inOutbyte = new byte[leaveByte]; 106 bInStream.read(inOutbyte, 0, leaveByte); 107 outputstream.write(inOutbyte); 108 outputstream.closeEntry(); //Closes the current ZIP entry and positions the stream for writing the next entry 109 bInStream.close(); //關閉 110 inStream.close(); 111 } 112 } 113 else 114 { 115 throw new ServletException("檔案不存在!"); 116 } 117 } 118 catch(IOException e) 119 { 120 throw e; 121 } 122 } 123 /** 124 * 下載打包的檔案 125 * @param file 126 * @param response 127 */ 128 public void downloadZip(File file,HttpServletResponse response) { 129 try { 130 // 以流的形式下載檔案。 131 BufferedInputStream fis = new BufferedInputStream(new FileInputStream(file.getPath())); 132 byte[] buffer = new byte[fis.available()]; 133 fis.read(buffer); 134 fis.close(); 135 // 清空response 136 response.reset(); 137 138 OutputStream toClient = new BufferedOutputStream(response.getOutputStream()); 139 response.setContentType("application/octet-stream"); 140 response.setHeader("Content-Disposition", "attachment;filename=" + file.getName()); 141 toClient.write(buffer); 142 toClient.flush(); 143 toClient.close(); 144 file.delete(); //將生成的伺服器端檔案刪除 145 } 146 catch (IOException ex) { 147 ex.printStackTrace(); 148 } 149 }
單個檔案的下載直接下載檔案即可,使用java自帶的FileOutputStream就能實現,可以從上面的批量下載中提取單個檔案下載的方法。
涉及到檔名稱編碼的問題,這裡提供一個格式化中文字串的方法。
1 public static String toUtf8String(String s){ 2 StringBuffer sb = new StringBuffer(); 3 for (int i = 0;i < s.length(); i++){ 4 char c = s.charAt(i); 5 if (c >= 0 && c <= 255) 6 { 7 sb.append(c); 8 } 9 else{ 10 byte[] b; 11 try 12 { 13 b = Character.toString(c).getBytes("utf-8"); 14 } 15 catch (Exception ex) { 16 b = new byte[0]; 17 } 18 for (int j = 0; j < b.length; j++) { 19 int k = b[j]; 20 if (k < 0) k += 256; 21 sb.append("%" + Integer.toHexString(k).toUpperCase()); 22 } 23 } 24 } 25 return sb.toString(); 26 }
第一次寫,歡迎各位大牛指點。如有不正之處歡迎指出。希望對有需要的園友有幫助,筆者將不甚歡喜!