jakarta upload File

      在〈jakarta upload File〉中尚無留言

Tomcat 也可以上傳檔案到伺服器,而且檔案的儲存位置可以超出網站的根目錄。雖說這有安全性的問題,但只要處理的好,其實非常方便。致於PHP,則只能把檔案儲存在網站根目錄之內,如果要超出網站根目錄,在Linux下就要使用 mount –bind 把根目錄外的目錄掛載到根目錄之內,而在Windows下則要使用junction建立連結。

Tomcat 自10版開始,為了避開 Java EE版權的束縛,全面改用Jakarta EE, 套件也由 javax 改寫成 jakarta。也就是說,9版及之前的版本都是使用 import javax套件,到了10版就必需使用 import jakarta套件。

也因如此,Tomcat 9 傳送檔案的代碼就無法在 Tomcat 10 運作。可是目前 95% 以上的網站都只說明 Tomcat 9 的傳送方式。致於如何在Tomcat 10運作,很難在網路上查得。所以本篇全部以 Tomcat 10 的版本作說明。

上傳套件

上傳檔案可以套用Apache開發的二個套件,分別為 commons-fileupload及commons-io。

當然,不一定要使用這二個套件,可以直接使用 Java 的 InputStream/OutputStream 進行讀取。但考慮到傳送中網路中斷造成整個 app卡住,甚至閃退問題,還是建議使用此二個套件,因為這二個套件都考慮到了我們意想不到的例外。如果有興趣直接使用Stream的朋友,可以參考如下網友的說明。

Stream 直接傳送參考網站 :
https://blog.judysocute.com/2020/03/13/%E7%AC%AC-6-%E9%80%B1-servlet-%E6%AA%94%E6%A1%88%E8%99%95%E7%90%86/
https://openhome.cc/Gossip/JavaEssence/InputStreamOutputStream.html

下載 fileupload及commonsIO的官網如下

fileupload : http://commons.apache.org/proper/commons-fileupload/ 目前最新版本為1.4版
commons IO : https://commons.apache.org/proper/commons-io/ 目前最新版本為 2.10.0版

請將二個檔解開後,將jar copy 到C:\Program Files\Apache Software Foundation\Tomcat 10.0\lib 之下

設定環境變數

請到系統設定新增環境變數

CATALINA_HOME : C:\Program Files\Apache Software Foundation\Tomcat 10.0
CLASSPATH : 新增如下三個值
C:\Program Files\Apache Software Foundation\Tomcat 10.0\lib\servlet-api.jar
C:\Program Files\Apache Software Foundation\Tomcat 10.0\lib\commons-fileupload-1.4.jar
C:\Program Files\Apache Software Foundation\Tomcat 10.0\lib\commons-io-2.10.0.jar

如果是在Linux之下,則在 /etc/profile下新增如下

export CLASSPATH=/opt/tomcat/lib/servlet-api.jar:/opt/tomcat/lib/commons-fileupload-1.4.jar:/opt/tomcat/lib/commons-io-2.10.0.jar

上述的 CLASSPATH 設定,主要是在手動使用 javac 編譯時,所要參考的 jar檔,所以一定要設定。

WEB-INF 目錄

在網頁根目錄下,必需產生如下WEB_INF的目錄,請手動一個一個加入

thomas #網頁根目錄
WEB-INF #設定檔子目錄
classes # .class存放位置
web.xml # 設定檔
upload.jsp
UploadServlet.java
messag.jsp

web.xml

web.xml只是描述 java 類別與網頁中所對應的url網址,並不會自動進行編譯,編譯的動作需手動操作,將於後面說明。

web.xml的設定如下,請注意紅色的部份一定要寫入,否則會出現 “Unable to process parts as no multi-part configuration has been provided” 例外錯誤。

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee
https://jakarta.ee/xml/ns/jakartaee/web-app_5_0.xsd"
version="5.0">
<servlet>
<servlet-name>UploadServlet</servlet-name>
<servlet-class>UploadServlet</servlet-class>
<multipart-config>
<max-file-size>20848820</max-file-size>
<max-request-size>418018841</max-request-size>
<file-size-threshold>1048576</file-size-threshold>
</multipart-config>
</servlet>

<servlet-mapping>
<servlet-name>UploadServlet</servlet-name>
<url-pattern>/UploadServlet</url-pattern>
</servlet-mapping>
</web-app>

代碼

上傳表單 upload.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>文件上傳表單</title>
</head>
<body>
<h1>文件上傳表單</h1>
<form method="post" action="/UploadServlet" enctype="multipart/form-data">
帳號 : <input type="text" name="txtAccount"><br>
密碼 : <input type="password" name="txtPassword"><br><br>
請選擇文件:<input type="file" name="photo" /><br/><br/>
<input type="submit" value="上傳" />
</form>
</body>
</html>

上傳代碼 UploadServlet.java

UploadServlet必需覆寫doPost() 或 doGet()方法,如下所示。

另請注意藍色的部份,這就是跟其它網站不一樣的地方,需全改成jakarta才可在Tomcat 10正常運作

//package com.asuscomm.mahaljsp;

import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;
import java.util.Collection;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.Part;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;

@WebServlet("/UploadServlet")
public class UploadServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
    private static final String UPLOAD_DIRECTORY = "upload";
    private static final int MEMORY_THRESHOLD   = 1024 * 1024 * 3;  // 3MB
    private static final int MAX_FILE_SIZE      = 1024 * 1024 * 40; // 40MB
    private static final int MAX_REQUEST_SIZE   = 1024 * 1024 * 50; // 50MB
 
    protected void doPost(HttpServletRequest request,HttpServletResponse response) throws ServletException, IOException {
	request.setCharacterEncoding("UTF-8"); 
	DiskFileItemFactory factory = new DiskFileItemFactory();
	factory.setSizeThreshold(MEMORY_THRESHOLD);
	factory.setRepository(new File(System.getProperty("java.io.tmpdir")));
	ServletFileUpload upload = new ServletFileUpload(factory);
	upload.setFileSizeMax(MAX_FILE_SIZE);
	upload.setSizeMax(MAX_REQUEST_SIZE);
	upload.setHeaderEncoding("UTF-8"); 
        String uploadPath = getServletContext().getRealPath("/") + UPLOAD_DIRECTORY;
	PrintWriter out=response.getWriter();
         
        File uploadDir = new File(uploadPath);
        if (!uploadDir.exists()) {
            uploadDir.mkdir();
        }

	out.println("<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Transitional//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'>");
	out.println("<html xmlns='http://www.w3.org/1999/xhtml'>");
	out.println("<head>");
	out.println("<meta http-equiv='Content-Type' content='text/html; charset=utf-8' />");
	out.println("</head>");
	out.println("<body>");
 
        try {
            //@SuppressWarnings("unchecked")
	    String userAccount=request.getParameter("txtAccount");
	    String userPassword=request.getParameter("txtPassword");
	    if(userAccount.equals("student") && userPassword.equals("1234")){
		Part part=request.getPart("photo");
		String fileName=part.getSubmittedFileName();
		if(!fileName.equals("")){
                    String fullName=uploadPath+File.separator+fileName;
		    part.write(fullName);
		    out.println("儲存檔案成功! 儲存位置 : "+ fullName);
		}
		else{
		    out.println("沒有指定上傳檔案");
		}
		//below could upload multi-file
		/*
		final Collection parts = request.getParts();			
		for (final Part part:parts){
		    String fileName=part.getSubmittedFileName();
		    if(!fileName.equals("")){				
                        part.write(uploadPath+File.separator+fileName);
		    }
		}
		*/
	    }
	    else{
	        out.println("帳號或密碼錯誤");
	    }
        } 
catch (Exception ex) { out.println("error:"+ex.getMessage()); } out.println("</body>"); out.println("</html>"); } }

編譯

進入Dos模式,再進入網頁根目錄下,看到 UploadServlet.java 後,下達

javac -encoding utf-8 UploadServlet.java

此時在網頁根目錄下會產生UploadServlet.class檔案,再把此檔案copy到 classes目錄中。記得最後要重新啟動Tomcat Server。

然後在瀏覽器上輸入 http://localhost:8080/upload.jsp,隨便上傳一個檔,就可以看到檔案被上傳到網站根目錄下 upload 的目錄。

Linux注意事項

權限問題

Tomcat使用tomcat:tomcat進行存取,所以如果希望當前登入者 student 帳號也能存取檔案的話,可以將 student 帳號加入tomcat群組。

sudo usermod -a -G tomcat student

另外因為Tomcat一般都會架設在nginx/Apache之下,所以為了讓php的網頁也能讀取到tomcat上傳的檔案,建議也將www-data帳號加入 tomcat群組之中。

sudo usermod -a -G tomcat www-data

Linux編譯

編譯java時,請記得如上說明,將目前帳號加入tomcat群組後,再使用底下指令

javac -encoding utf-8 UploadServlet.java

其他參考

Stream寫法 :
https://blog.judysocute.com/2020/03/13/%E7%AC%AC-6-%E9%80%B1-servlet-%E6%AA%94%E6%A1%88%E8%99%95%E7%90%86/
https://openhome.cc/Gossip/JavaEssence/InputStreamOutputStream.html

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *