1. 程式人生 > >Javaweb學習筆記——(二十二)——————檔案上傳、下載、Javamail

Javaweb學習筆記——(二十二)——————檔案上傳、下載、Javamail

 檔案上傳概述      1.檔案上傳的作用          例如網路硬碟,就是用來上傳下載檔案的。          在網路瀏覽器中,時常需要上傳照片

     2.檔案上傳對頁面的要求          上傳檔案的要求比較多。需要記住              1.必須使用表單,而且不能是超連結。              2.表單的method必須是POST,不能是GET;              3.表單的enctype必須是meltipart/form-data              4.在表單中新增file表單欄位,即<input type="file".../>

             <form action="${pageContext.request.contextPath }/FileUploadServlet" method="post" enctype="multipart/form-data">                 使用者名稱:<input type="text" name="username"/><br/>                 檔案1:<input type="file" name="file1"/><br/>                 檔案2:<input type="file" name="file2"/><br/>                 <input type="submit" value="提交"/>             </form>

    3.比對檔案上傳表單和普通文字表單的區別         通過httpWatch檢視“檔案上傳表單”和“普通文字表單”的區別             *檔案上傳表單是enctype="multipart/form-data",表示多部件表單資料;             *普通檔案表單可以不設定enctype屬性:                 **當method="post"時,enctype的預設值為application/x-www-form-urlencoded,表示使用url編碼正文;                 **當method="get"時,enctype的預設值為null,沒有正文,所以就不需要enctype了。

        對普通文字表單的測試:             <form action="${pageContext.request.contextPath }/FileUploadServlet" method="post">                     使用者名稱:<input type="text" name="username"/><br/>                     檔案1:<input type="file" name="file1"/><br/>                     檔案2:<input type="file" name="file2"/><br/>                     <input type="submit" value="提交"/>             </form>

            通過httpWatch測試,查看錶單的請求資料正文,我們發現請求中只有檔名稱,而沒有檔案內容。也就是說,當表單的enctype不是multipart/form             -data時,請求中不包含檔案內容,而只用檔案的民粹,這說明普通文字表單中input:file和input:text沒有什麼區別。

        對檔案上傳表單的測試                 <form action="${pageContext.request.contextPath }/FileUploadServlet" method="post" enctype="multipart/form-data">                     使用者名稱:<input type="text" name="username"/><br/>                     檔案1:<input type="file" name="file1"/><br/>                     檔案2:<input type="file" name="file2"/><br/>                     <input type="submit" value="提交"/>                 </form>             通過httpWatch測試,查看錶單的請求資料正文部分,發現正文部位是由多個部件組成,每個部件對應一個表單欄位,每個部件都有自己的頭資訊。頭資訊下面是空行,空行下面是欄位是正文部分。多個部件之間使用隨機生成的分割線隔開。             文字欄位的頭資訊中只包含一條頭資訊,即Content-Disposition,這個頭資訊的值有兩部分,第一部分是固定的,即form-data,第二部分為欄位的名稱。在空行後面就是正文部分了,正文部分就是在文字框中填寫的內容。             檔案欄位的頭資訊中包含了頭資訊,Content-Disposition和Content-Type。Content-Disposition中多出了一個filename,它指定的是上傳的檔名稱。而Content-Type指定的是上傳檔案的型別。檔案欄位的正文部分就是檔案的內容。

            請注意,因為我們上傳的檔案都是普通文字檔案,即txt檔案,所以在httpWatch中是可以正常顯示的,如果上傳的是exe、mp3等檔案,那麼在httpWatch中看到的就是亂碼了。

    4.檔案上傳對Servlet的要求         當提交的表單是檔案上傳表單時,那麼對Servlet也是有要求的。首先我們要肯定一點,檔案上傳表單的資料也是被封裝到request物件中的。         request.getParameter(String)方法獲取指定的表單欄位字元內容,單檔案上傳表單已經不再是字元內容,而是位元組內容,所以失效。

        這時可以使用request的getInputStream()方法獲取ServletInputStream物件,它是InputStream的子類,這個ServletInputStream物件對應整個表單的正文部分(從第一個分隔線開始,到最後),這說明我們需要的解析流中的資料。當然解析它是很麻煩的一件事情,而Apache已經幫我們提供了了解它的工具:commons-fileupload。

        可以嘗試吧request.getInputStream()這個流中的內容打印出來,再對比httpWatch中請求資料。             public void doPost(HttpServletRequest request, HttpServletResponse response)                 throws ServletException, IOException {                 InputStream in = request.getInputStream();                 String s = IOUtils.toString(in);                 System.out.println(s);             }

            -----------------------------7ddd3370ab2             Content-Disposition: form-data; name="username"

            hello             -----------------------------7ddd3370ab2             Content-Disposition: form-data; name="file1"; filename="a.txt"             Content-Type: text/plain

            aaa             -----------------------------7ddd3370ab2             Content-Disposition: form-data; name="file2"; filename="b.txt"             Content-Type: text/plain

            bbb             -----------------------------7ddd3370ab2--

    Commons-fileupload         為什麼使用filleupload:             上傳檔案的要求比較多,需要記一下:                 *必須是POST表單;                 *表單的enctype必須是multipart/form-data;                 *在表單中新增file表單欄位,即<input type="file" .../>

        Servlet的要求:             *不能再使用request.getParameter()來獲取表單資料;             *可以使用request.getInputStream()得到所有的表單資料,而不是一個表單項的資料;             *這說明不適用fileupload,我們需要自己來對request.getInputStream()的內容進行解析。

        1.fileupload概述             fileupload是由apache的commons元件提供的上傳元件。它最主要的工作就是幫我們解析request.getInputStream()。             fileupload元件需要的jar包有:                 *commons-fileupload.jar,核心包;                 *commons-io.jar,依賴包。

        2.fileupload簡單應用             fileupload的核心類有:DiskFileItemFactory、ServletFileUpload、FileItem。             使員工fileupload元件的步驟如下:                 1.建立工廠類DiskFileItemFactory物件:DiskFileItemFactory factory = new DiskFileItemFactory()                 2.使用工廠建立解析器物件:ServetFileUpload fileUpload = new ServletFileUpload(factory);                 3.使用解析器來解析request物件:List<FileItem> list = fileUpload.parseRquest(request);

            隆重介紹FileItem類,它才是我們最終想要的結果。一個FileItem物件對應一個表單項(表單欄位)。一個表單中存在檔案欄位和普通欄位,可以使用FileItem類的isFormField()方法來判斷表單欄位是否為普通欄位,如果不是普通欄位,那麼就是檔案欄位了。                 *String getName():獲取檔案欄位的檔名稱;                 *String getString():獲取欄位的內容,如果是檔案欄位,那麼獲取的是檔案內容,當然上傳的檔案必須是文字檔案;                 *String getFieldName():獲取欄位名稱,例如:<input type="text" name="username"/>,返回的是username;                 *String getContentType():獲取上傳的檔案的型別,例如:text/plain。                 *int getSize():獲取上傳檔案的大小;                 *boolean isFormField():判斷當前表單欄位是否為普通文字欄位,如果返回false,說明是檔案欄位;                 *InputStream getInputStream():獲取上傳檔案對應的輸入流;                 *void write(File):把上傳的檔案儲存到指定檔案中。

        3.簡單上傳示例             寫一個簡單的上傳示例:                 *表單包含一個使用者名稱欄位,已經一個檔案欄位;                 *Servlet儲存上傳的檔案到uploads目錄,顯示使用者名稱,檔名,檔案大小,檔案型別。

            第一步:                 完成index.jsp,只需要一個表單。注意表單必須是post的,而且enctype必須是mulitpart/form-data的。                     <form action="${pageContext.request.contextPath }/FileUploadServlet" method="post" enctype="multipart/form-data">                         使用者名稱:<input type="text" name="username"/><br/>                         檔案1:<input type="file" name="file1"/><br/>                         <input type="submit" value="提交"/>                     </form>

            第二步:                 完成FileUploadServlet                         public void doPost(HttpServletRequest request, HttpServletResponse response)                                 throws ServletException, IOException {                             // 因為要使用response列印,所以設定其編碼                             response.setContentType("text/html;charset=utf-8");                                                          // 建立工廠                             DiskFileItemFactory dfif = new DiskFileItemFactory();                             // 使用工廠建立解析器物件                             ServletFileUpload fileUpload = new ServletFileUpload(dfif);                             try {                                 // 使用解析器物件解析request,得到FileItem列表                                 List<FileItem> list = fileUpload.parseRequest(request);                                 // 遍歷所有表單項                                 for(FileItem fileItem : list) {                                     // 如果當前表單項為普通表單項                                     if(fileItem.isFormField()) {                                         // 獲取當前表單項的欄位名稱                                         String fieldName = fileItem.getFieldName();                                         // 如果當前表單項的欄位名為username                                         if(fieldName.equals("username")) {                                             // 列印當前表單項的內容,即使用者在username表單項中輸入的內容                                             response.getWriter().print("使用者名稱:" + fileItem.getString() + "<br/>");                                         }                                     } else {//如果當前表單項不是普通表單項,說明就是檔案欄位                                         String name = fileItem.getName();//獲取上傳檔案的名稱                                         // 如果上傳的檔名稱為空,即沒有指定上傳檔案                                         if(name == null || name.isEmpty()) {                                             continue;                                         }                                         // 獲取真實路徑,對應${專案目錄}/uploads,當然,這個目錄必須存在                                         String savepath = this.getServletContext().getRealPath("/uploads");                                         // 通過uploads目錄和檔名稱來建立File物件                                         File file = new File(savepath, name);                                         // 把上傳檔案儲存到指定位置                                         fileItem.write(file);                                         // 列印上傳檔案的名稱                                         response.getWriter().print("上傳檔名:" + name + "<br/>");                                         // 列印上傳檔案的大小                                         response.getWriter().print("上傳檔案大小:" + fileItem.getSize() + "<br/>");                                         // 列印上傳檔案的型別                                         response.getWriter().print("上傳檔案型別:" + fileItem.getContentType() + "<br/>");                                     }                                 }                             } catch (Exception e) {                                 throw new ServletException(e);                             }                          }

    檔案上傳之細節         1.把上傳的檔案放到WEB-INF目錄下             如果沒有把使用者上傳的檔案存放到WEB-INF目錄下,那麼使用者就可以通過瀏覽器直接訪問上傳的檔案,這是非常危險的。             假如說使用者上傳了一個a.jsp檔案,然後使用者在通過瀏覽器去訪問這個a.jsp檔案,那麼就會執行a.jsp中的內容,如果在a.jsp中有如下語句:Runtime.getRuntime().exec("shutdown -s -t 1");,那麼系統立馬關機

            通常我們會在WEB-INF目錄下建立一個uploads目錄來存放上傳的檔案,而在Servlet中找到這個目錄需要使用ServletoContext的getRealPath(String)方法,例如在我的upload1專案中有如下語句:                 ServletContext servletContext = this.getServletContext();                 String savePath = servletContext.getRealPath("/WEB-INF/uploads");                 其中savePath為:F:\tomcat6_1\webapps\upload1\WEB-INF\uploads

        2.檔名稱(完整路徑、檔名稱)             上傳檔名稱可能是完整路徑:                 IE6獲取的上傳檔名稱是完整路徑,而其他瀏覽器獲取的上傳檔名稱只是檔名稱而已。瀏覽器差異的問題我們還是需要處理一下。                     String name = file1FileItem.getName();                     response.getWriter().print(name);

                使用不同瀏覽器測試,其中IE6就會返回上傳檔案的完整路徑,不知道IE6怎麼操作,這就給我們帶來了很大的麻煩,就是需要處理一下這個問題。                 處理這一問題也很簡單,無論是否為完整路徑,我們都去擷取最後一個“\\”後面的內容就可以了。                     String name = file1FileItem.getName();                     int lastIndex = name.lastIndexOf("\\");//獲取最後一個“\”的位置                     if(lastIndex != -1) {//注意,如果不是完整路徑,那麼就不會有“\”的存在。                         name = name.substring(lastIndex + 1);//獲取檔名稱                     }                     response.getWriter().print(name);

        3.中文亂碼問題             上傳檔名稱包含中文:                 當上傳檔名稱中包含中文:                     當上傳的誰的名稱中包含中文時,需要設定編碼,commons-fileupload元件為我們提供了兩種設定編碼的方式:                         *request.setCharacterEncoding(Strnig):這種方式是我們最為熟悉的一種;                         *fileUpload.setHeaderEncoding(String):這種方式的優先順序高於前一種。

                上傳檔案的檔案內容包含中文:                     通常我們會把使用者上傳的檔案儲存到uploads目錄下,但是如果使用者上傳了同名檔案?,這回出現覆蓋現象。處理這一問題的手段是使用UUID生產唯一名稱,然後再使用“_”連線檔案上傳的原始名稱。                     例如使用者上傳的檔案是“123.jpg”,在通過處理後,檔名稱為“891b3881395f4175b969256a3f7b6e10_123.jpg”,這種手段不會使檔案丟失副檔名,並且因為UUID的唯一性,上傳的檔案同名,但是在伺服器端是不會出現同名問題的。                         public void doPost(HttpServletRequest request, HttpServletResponse response)                                 throws ServletException, IOException {                             request.setCharacterEncoding("utf-8");                             DiskFileItemFactory dfif = new DiskFileItemFactory();                             ServletFileUpload fileUpload = new ServletFileUpload(dfif);                             try {                                 List<FileItem> list = fileUpload.parseRequest(request);                                 //獲取第二個表單項,因為第一個表單項是username,第二個才是file表單項                                 FileItem fileItem = list.get(1);                                 String name = fileItem.getName();//獲取檔名稱                                                                  // 如果客戶端使用的是IE6,那麼需要從完整路徑中獲取檔名稱                                 int lastIndex = name.lastIndexOf("\\");                                 if(lastIndex != -1) {                                     name = name.substring(lastIndex + 1);                                 }                                                                  // 獲取上傳檔案的儲存目錄                                 String savepath = this.getServletContext().getRealPath("/WEB-INF/uploads");                                 String uuid = CommonUtils.uuid();//生成uuid                                 String filename = uuid + "_" + name;//新的檔名稱為uuid + 下劃線 + 原始名稱                                                                  //建立file物件,下面會把上傳檔案儲存到這個file指定的路徑                                 //savepath,即上傳檔案的儲存目錄                                 //filename,檔名稱                                 File file = new File(savepath, filename);                                                                  // 儲存檔案                                 fileItem.write(file);                             } catch (Exception e) {                                 throw new ServletException(e);                             }                          }

        5.一個目錄不能存放過多的檔案(存放目錄打散)             一個目錄下不應該存放過多的檔案,一般一個目錄下存放1000個檔案就是上限了,如果在多,那麼開啟目錄的時候就會出現卡頓現象。             也就是說,我們需要把上傳的檔案放到不同的目錄中。但是也不能為每個上傳的檔案設定一個目錄,這種方式會導致目錄過多。所以應該採用某種演算法進行“打散”操作。             打散的方式有很多,例如使用日期進行打散,每天生成一個目錄,也可以使用檔名的首字母來生成目錄,相同首字母的檔案放到同一目錄下。             日期打散演算法:如果某一天上傳的檔案過多,那麼也會出現一個目錄檔案過多的情況;             首字打散演算法:如果檔名是中文的,因為中文過多,所有會導致目錄過多的現象。

            這裡我們使用hash演算法進行打散操作:                 1.獲取檔名稱的hashCode、int hCode = name.hashCode();                 2.獲取hCode的第4位,然後轉換成16進位制字元;                 3.獲取hCode的5~8位,然後轉換成16進位制字元;                 4.使用這兩個16進位制的字元生成目錄鏈。例如第4位字元為“5”

            這種演算法的好處是,在uploads目錄下最多生成16個目錄,而每個目錄下最多再生成16個目錄,即256個目錄,所有上傳的檔案都放到這256個目錄下。如果每個目錄上限為1000個檔案,那麼一共可以有256000個檔案。

            例如上傳檔名稱為:新建 文字文件.txt,那麼把“新建 文字文件.txt”的雜湊碼獲取到,再獲取雜湊碼的低4位,和5~8位。假如第4位為9,5~8位為1,那麼檔案的儲存路徑為uploads/9/1/。                 int hCode = name.hashCode();//獲取檔名的hashCode                 //獲取hCode的低4位,並轉換成16進位制字串                 String dir1 = Integer.toHexString(hCode & 0xF);                 //獲取hCode的低5~8位,並轉換成16進位制字串                 String dir2 = Integer.toHexString(hCode >>> 4 & 0xF);                 //與檔案儲存目錄連線成完整路徑                 savepath = savepath + "/" + dir1 + "/" + dir2;                 //因為這個路徑可能不存在,所以建立成File物件,再建立目錄鏈,確保目錄在儲存檔案之前已經存在                 new File(savepath).mkdirs();

        6.上傳的單個檔案的大小限制             限制上傳檔案的大小很簡單,ServletFileUpload類的setFileSizeMax(long)就可以了。引數就是上傳檔案的上限位元組數,例如servletFileUpload.setFileSizeMax(1024*10)表示上限為10kb。             一旦上傳的檔案超出了上限,那麼就會丟擲FileUploadBase.FileSizeLimitExceededException異常。我們可以在Servlet中獲取這個異常,然後向頁面輸出“上傳的檔案超出限制”。                     public void doPost(HttpServletRequest request, HttpServletResponse response)                             throws ServletException, IOException {                         request.setCharacterEncoding("utf-8");                         DiskFileItemFactory dfif = new DiskFileItemFactory();                         ServletFileUpload fileUpload = new ServletFileUpload(dfif);                         // 設定上傳的單個檔案的上限為10KB                         fileUpload.setFileSizeMax(1024 * 10);                         try {                             List<FileItem> list = fileUpload.parseRequest(request);                             //獲取第二個表單項,因為第一個表單項是username,第二個才是file表單項                             FileItem fileItem = list.get(1);                             String name = fileItem.getName();//獲取檔名稱                                                          // 如果客戶端使用的是IE6,那麼需要從完整路徑中獲取檔名稱                             int lastIndex = name.lastIndexOf("\\");                             if(lastIndex != -1) {                                 name = name.substring(lastIndex + 1);                             }                                                          // 獲取上傳檔案的儲存目錄                             String savepath = this.getServletContext().getRealPath("/WEB-INF/uploads");                             String uuid = CommonUtils.uuid();//生成uuid                             String filename = uuid + "_" + name;//新的檔名稱為uuid + 下劃線 + 原始名稱                                                          int hCode = name.hashCode();//獲取檔名的hashCode                             //獲取hCode的低4位,並轉換成16進位制字串                             String dir1 = Integer.toHexString(hCode & 0xF);                             //獲取hCode的低5~8位,並轉換成16進位制字串                             String dir2 = Integer.toHexString(hCode >>> 4 & 0xF);                             //與檔案儲存目錄連線成完整路徑                             savepath = savepath + "/" + dir1 + "/" + dir2;                             //因為這個路徑可能不存在,所以建立成File物件,再建立目錄鏈,確保目錄在儲存檔案之前已經存在                             new File(savepath).mkdirs();                                                          //建立file物件,下面會把上傳檔案儲存到這個file指定的路徑                             //savepath,即上傳檔案的儲存目錄                             //filename,檔名稱                             File file = new File(savepath, filename);                                                          // 儲存檔案                             fileItem.write(file);                         } catch (Exception e) {                             // 判斷丟擲的異常的型別是否為FileUploadBase.FileSizeLimitExceededException                             // 如果是,說明上傳檔案時超出了限制。                             if(e instanceof FileUploadBase.FileSizeLimitExceededException) {                                 // 在request中儲存錯誤資訊                                 request.setAttribute("msg", "上傳失敗!上傳的檔案超出了10KB!");                                 // 轉發到index.jsp頁面中!在index.jsp頁面中需要使用${msg}來顯示錯誤資訊                                 request.getRequestDispatcher("/index.jsp").forward(request, response);                                 return;                             }                             throw new ServletException(e);                         }                      }                  7.上傳檔案的總大小限制             上傳檔案的表單中可能允許上傳多個檔案,例如:                 有時我們需要限制一個請求的大小。也就是說這個請求的最大位元組數(所有表單項之和)實現這一功能也很簡單,只需要呼叫ServletFileUpload類的setSizeMax(long)方法即可。                 例如fileUpload.setSizeMax(1024*10),顯示整個請求的上限為10kb。當請求大小超出了10kb時,ServletFileUploa類的parseRequest()方法會丟擲FileUploadBase.SizeLimitException異常。

        8.快取大小與臨時目錄             一種現象:如果上傳一個藍光電影,先把電影儲存到電影中,然後再通過記憶體copy到伺服器硬碟上,那麼你的記憶體能吃下嗎?             所以fileUpload元件不可能吧檔案都保留到記憶體中,fileUpload會判斷檔案大小是否超出10kb,如果是,那麼就把檔案儲存到硬碟中,如果沒超過,那麼就儲存到記憶體中。             10kb是fileUpload的預設值,我們可以來設定它。             當檔案儲存到硬碟時,fileUpload是吧檔案儲存到系統臨時目錄,當然你也可以去設定臨時目錄。                 public void doPost(HttpServletRequest request, HttpServletResponse response)                         throws ServletException, IOException {                     request.setCharacterEncoding("utf-8");                     DiskFileItemFactory dfif = new DiskFileItemFactory(1024*20, new File("F:\\temp"));                     ServletFileUpload fileUpload = new ServletFileUpload(dfif);                                          try {                         List<FileItem> list = fileUpload.parseRequest(request);                         FileItem fileItem = list.get(1);                         String name = fileItem.getName();                         String savepath = this.getServletContext().getRealPath("/WEB-INF/uploads");                                                  // 儲存檔案                         fileItem.write(path(savepath, name));                     } catch (Exception e) {                         throw new ServletException(e);                     }                  }                                  private File path(String savepath, String filename) {                     // 從完整路徑中獲取檔名稱                     int lastIndex = filename.lastIndexOf("\\");                     if(lastIndex != -1) {                         filename = filename.substring(lastIndex + 1);                     }                                          // 通過檔名稱生成一級、二級目錄                     int hCode = filename.hashCode();                     String dir1 = Integer.toHexString(hCode & 0xF);                     String dir2 = Integer.toHexString(hCode >>> 4 & 0xF);                     savepath = savepath + "/" + dir1 + "/" + dir2;                     // 建立目錄                     new File(savepath).mkdirs();                                          // 給檔名稱新增uuid字首                     String uuid = CommonUtils.uuid();                     filename = uuid + "_" + filename;                                          // 建立檔案完成路徑                     return new File(savepath, filename);                 }

檔案下載     1.通過Servlet下載1         被下載的資源必須放到WEB-INF目錄下,(只要使用者不能通過瀏覽器直接訪問就ok),然後通過Servlet完成下載,         在jsp頁面中給出超連結,連線到DownloadServlet,並提供要下載的檔名稱。然後DownloadServlet獲取檔案的真實路徑,然後把檔案寫入到response.getOutputStream()流中。

        download.jsp               <body>                 This is my JSP page. <br>                 <a href="<c:url value='/DownloadServlet?path=a.avi'/>">a.avi</a><br/>                 <a href="<c:url value='/DownloadServlet?path=a.jpg'/>">a.jpg</a><br/>                 <a href="<c:url value='/DownloadServlet?path=a.txt'/>">a.txt</a><br/>               </body>

        DownloadServlet.java                 public void doGet(HttpServletRequest request, HttpServletResponse response)                         throws ServletException, IOException {                     String filename = request.getParameter("path");                     String filepath = this.getServletContext().getRealPath("/WEB-INF/uploads/" + filename);                     File file = new File(filepath);                     if(!file.exists()) {                         response.getWriter().print("您要下載的檔案不存在!");                         return;                     }                     IOUtils.copy(new FileInputStream(file), response.getOutputStream());                 }

        上面程式碼有如下問題:             *可以下載a.avi,但是在下載框中的檔名稱為DownloadServlet;             *不能下載a.jpg和a.txt,而是在頁面中顯示他們。

    2.通過Servlet下載2         下面來處理上一例中的問題,讓下載框中可以顯示正確的檔名稱,已經可以下載a.jsp和a.txt檔案。         通過新增content-disposition頭來處理上面問題。當設定了content-disposition頭後,瀏覽器就會彈出下載框。         而且還可以通過content-disposition頭來指定下載檔案的名稱                 String filename = request.getParameter("path");                 String filepath = this.getServletContext().getRealPath("/WEB-INF/uploads/" + filename);                 File file = new File(filepath);                 if(!file.exists()) {                     response.getWriter().print("您要下載的檔案不存在!");                     return;                 }                 response.addHeader("content-disposition", "attachment;filename=" + filename);                 IOUtils.copy(new FileInputStream(file), response.getOutputStream());             雖然上面的程式碼已經處理txt和jpg等檔案的下載問題,並且也處理了下載框中顯示檔名稱的問題,但是如果下載的檔名稱是中文的,那麼還是不行的。

    3.通過Serlvet下載3         下面是處理在下載框中顯示中文的問題         其實這一問題很簡單,只需要通過URL來編碼中文即可。

            download.jsp                 <a href="<c:url value='/DownloadServlet?path=這個殺手不太冷.avi'/>">這個殺手不太冷.avi</a><br/>                 <a href="<c:url value='/DownloadServlet?path=白冰.jpg'/>">白冰.jpg</a><br/>                 <a href="<c:url value='/DownloadServlet?path=說明文件.txt'/>">說明文件.txt</a><br/>

            DownloadServlet.java                 String filename = request.getParameter("path");                 // GET請求中,引數中包含中文需要自己動手來轉換。                 // 當然如果你使用了“全域性編碼過濾器”,那麼這裡就不用處理了                 filename = new String(filename.getBytes("ISO-8859-1"), "UTF-8");                                  String filepath = this.getServletContext().getRealPath("/WEB-INF/uploads/" + filename);                 File file = new File(filepath);                 if(!file.exists()) {                     response.getWriter().print("您要下載的檔案不存在!");                     return;                 }                 // 所有瀏覽器都會使用本地編碼,即中文作業系統使用GBK                 // 瀏覽器收到這個檔名後,會使用iso-8859-1來解碼                 filename = new String(filename.getBytes("GBK"), "ISO-8859-1");                 response.addHeader("content-disposition", "attachment;filename=" + filename);                 IOUtils.copy(new FileInputStream(file), response.getOutputStream());

JavaMail     *郵件協議     *teinet訪問郵件伺服器     *JavaMail

    郵件協議         1.收發郵件             發郵件是從客戶端吧郵件傳送到郵件伺服器,收郵件是吧郵件伺服器的郵件下載到客戶端。             在163、126、QQ、sohu、sina等網站註冊的Email賬戶,其實就是在郵件伺服器中註冊的。這些網站都有自己的郵件伺服器。

        2.郵件協議概述             與Http協議相同,收發郵件也是需要有傳輸協議的。                 *SMTP:(Simple Mail Transfer Protocol,簡單郵件傳輸協議)發郵件協議;                 *POP3:(Post Office Protocol Version 3,郵局協議第3版)收郵件協議;                 *IMAP:(Internet Message Access Protocol,因特網訊息訪問協議)收發郵件協議。

        3.理解郵件收發過程             把郵件伺服器理解為郵局,如果你需要寄一封信,那麼你需要把信封放到信筒中,這樣你的信就會“自動”到達郵局,郵局會把信郵寄到另一個地方的郵局中。然後這封信會被送到收信人的郵箱中。最終收信人需要自己經常檢視郵箱中是否有新的信件。             其實每個郵件伺服器都有SMTP伺服器和POP3伺服器構成,其中SMTP伺服器負責發郵件的請求,而POP3負責收郵件的請求。

            當然,有時會使用163的賬戶,向126的賬號傳送郵件。這時郵件是傳送到126的郵件伺服器,而對於163的郵件伺服器是不會儲存這封郵件的。

        4.郵件伺服器名稱             SMTP伺服器的埠號為25,伺服器名稱為smtp.xxx.xxx。             POP3伺服器的埠號為110,伺服器名稱為pop3.xxx.xxx。             例如:                 *163:smtp.163.com和pop3.163.com;                 *126:smtp.126.com和pop3.126.com;                 *qq:smtp.qq.com和pop3.qq.com;                 *sohu:smtp.sohu.com和pop3.sohu.com;                 *sina:smtp.sina.com和pop3.sina.com。

    telnet收發郵件         1.BASE64加密             BASE64是一種加密演算法,這總加密方式是可逆的。它的作用是使加密後的文字無法通過肉眼識別。java提供了sun.misc.BASE63Encoder這個類,用來對做Base64的加密和解密。                                  import org.apache.commons.codec.binary.Base64;

                public class Base64Utils {                     public static String encode(String s) {                         return encode(s, "utf-8");                     }                                          public static String decode(String s) {                         return decode(s, "utf-8");                     }                                          public static String encode(String s, String charset) {                         try {                             byte[] bytes = s.getBytes(charset);                             bytes = Base64.encodeBase64(bytes);                             return new String(bytes, charset);                         } catch (Exception e) {                             throw new RuntimeException(e);                         }                     }

                    public static String decode(String s, String charset) {                         try {                             byte[] bytes = s.getBytes(charset);                             bytes = Base64.decodeBase64(bytes);                             return new String(bytes, charset);                         } catch (Exception e) {                             throw new RuntimeException(e);                         }                     }                 }

        2.telnet發郵件             連線163的smtp伺服器:telnet smtp.163.com 25;             連線成功後需要如下步驟才能傳送郵件:                 1.與伺服器打招呼:ehlo你的名字                 2.發出登入請求:auth-login                 3.輸入加密後的郵箱名:([email protected])aXRjYXN0X2N4ZkAxNjMuY29t                 4.輸入加密後的郵箱密碼:(123456)aXRjYXN0                 5.輸入誰來發送郵件,即from:mail from:<[email protected]>                 6.輸入把郵件發給誰,即to:rcpt to:<[email protected]>                 7.傳送新增資料請求:data                 8.開始輸入資料,資料包含:from、to、subject、以及郵件內容,如果輸入結束後,以一個“.”為一行,表示輸入結束;                                      from:<[email protected]>                     to:<[email protected]>                     subject: 我愛上你了

                    我已經深深的愛上你了。                     .

                    注意:在標題和郵件正文之間要有一個空行。當要退出時,一定要以一個“.”為單行,表示輸入結束。                 9.最後一步:quit

        3.telnet收郵件             1.telnet收郵件的步驟                 pop3無需使用Base64加密。                 收郵件連線的伺服器是pop3.xxx.com,pop3協議的預設埠號為110。請注意。這與發郵件完全不同。如果你在163有郵箱賬號,那麼你想使用telnet收郵件,需要連線的伺服器是pop3.163.com。

                *連線pop3伺服器:telnet.pop3.163.com 110                 *user命令:user 使用者名稱,例如:user [email protected];                 *pass命令:pass 密碼,例如:pass 123456                 *stat命令:stat命令用來檢視郵箱中郵件的個數,所有郵件所佔的空間;                 *list命令:list命令用來檢視所有郵件,或指定郵件的狀態,例如:list 1是檢視第一封郵件的大小,list是檢視郵件列表,即列出所有郵件的編號,及大小;                 *retr命令:檢視指定郵件的內容,例如:retr 1#是檢視第一封郵件的內容;                 *dele命令:標記某郵件為刪除,單不是馬上刪除,而是在退出時才會真正的刪除;                 *quit命令:退出。如果在退出之前已經使用了dele命令標記了某些郵件,那麼會在退出時刪除它們。

    JavaMail         1.JavaMail概述             JavaMail是由SUN公司提供的專門針對郵件的api,主要的jar包:mail.jar、activation.jar。             在使用MyEclipse建立web專案時,需要小心。如果只是在web專案中使用JavaMail是沒有什麼問題的,釋出到Tomcat上執行一點問題都沒有。             但是在web專案中寫測試那就出問題了。             在MyEclipse中,會自動給web專案匯入javax.mail包中的類,但是不全(其實是隻有介面,而沒有介面的實現類),所以只靠MyEclipse中的類是不能執行JavaMail專案的,但是如果這時你再去自行匯入mail.jar時,就會出現衝突。             處理方案:到下面路徑中找到javaee.jar檔案,把javax.mail刪除。

        2.JavaMail中主要類             JavaMail中主要類:javax.mail.Session、javax.mail.internet.MimeMessage、javax.mail.Transport。             Session:表示會話,即客戶端與郵件伺服器之間的會話,想要獲得會話需要給出賬戶和密碼,當然還要給出伺服器名稱。在郵件伺服器中的Session物件,就相當於連線資料庫時的Commection物件。             MimeMessage:表示郵件類,它是Message的子類。它包含郵件的主題(標題)、內容,收件人地址、發件人地址,還可以設定抄送和暗送,甚至還可以設定附件。             Transport:用來發送郵件。它是傳送器。

        3.JavaMail之HelloWorld             在使用telnet發郵件時,還需要自己來處理Base64編碼對問題,但是使用Javamail就不必理會這些問題了,都有JavaMail來處理。

            第一步:獲取Session                 Session session = Session。getInstance(Properties prop, Authenticator auth);                 其中prop需要制定兩個鍵值,一個是制定伺服器主機名,另一個是指定是否需要認證。                 Properties prop = new Properties();                 prop.setProperty("mail.host", "smtp.qq.com");//設定伺服器主機名                 prop.setProperty("mail.smtp.auth", "true");//設定需要認證

                其中Authenticator是一個介面表示認證器,集校驗客戶端的身份。我們需要自己來實現這個介面,實現這個介面需要使用賬戶和密碼。                     Authenticator auth = new Authenticator(){                         public PasswordAuthentication getPasswordAuthentication(){                             return new PasswordAuthentication("1451388723", "123456");//使用者名稱和密碼                         }                     }

                通過上面的準備,現在可以獲取Session物件了:                     Session session = Session.getInstance(prop, auth);

            第二步:建立MimeMessage物件                 建立MimeMessage需要使用Session物件來建立:                     MimeMssage msg = new MimeMessage(session);                 然後需要設定發信人地址、收信人地址、主題,以及郵件正文。                     msg.setFrom(new InternetAddress("[email protected]"));//設定發信人                     msg.setRecipients(RecipientType.TO, "[email protected]", "[email protected]");//設定多個收信人                     msg.setRecipients(RecipientType.CC, "[email protected]","[email protected]");//設定多個抄送                     msg.setRecipients(RecipientType.BCC, "[email protected]");//設定暗送人                     msg.setSubject("這是一封測試郵件");//設定主題(標題)                     msg.setContent("當然是HelloWorld。","text/plain;charset=utf-8");//設定正文

            第三步:傳送郵件:                 Transport.send(msg);//傳送郵件

        4.JavaMail傳送帶有附件的郵件(瞭解)             一封郵件可以包含正文、附件N個,所有正文與N個附件都是有郵件的一個部分。             上面的HelloWorld案例中,只是傳送了帶有正文的郵件,所以在呼叫setContent()方法時直接設定了正文,如果想要傳送帶有附件郵件,那麼需要設定郵件的內容為MimeMultiPart。                 MimeMulitpart parts = new MimeMulitpart();//多部件物件,可以理解為是部件的集合                 msg.setContent(parts);//設定郵件的內容為多部件內容。                 然後我們需要把正文、N個附件建立為“主體部件”物件(MimeBodyPart),新增到MimeMuiltPart中即可。                 MimeBodyPart part1 = new MimeBodyPart();//建立一個部件                 part1.setContent("這是正文部分", "text/html;charset=utf-8");//給部件設定內容                 parts.addBodyPart(part1);//把部件新增到部件集中。

            下面我們建立一個附件:                 MimeBodyPart part2 = new MimeBodyPart();//建立一個部件                 part2.attachFile("d:\\a.jpg");//設定附件                 part2.setFileName("hello.jpg");//設定附件名稱                 part2.addBodyPart(part2);//把附件新增到部件集中

            注意:如果在設定名稱中,檔名稱中包含了中文的話,那麼需要使用MimeUitlity類來給中文編碼:                 part2.setFileName(MimeUitlity.encodeText("美女.jpg"));