顯示具有 multipart/form-data 標籤的文章。 顯示所有文章
顯示具有 multipart/form-data 標籤的文章。 顯示所有文章

2021年12月25日 星期六

使用 VBScript 上傳檔案( multipart/form-data http post)

這裡紀錄下使用 VBscript 上傳檔案 (httpPost multipart/form-data) 的方法,
在這邊是照著 multipart/form-data 協議來手動刻出所需的封包格式,
詳細可以參考:

這裡我們用 WSF (Window Script File) 檔配合 VBscript 程式來實作,
將以下程式碼存成 UTF-8 編碼格式的 .wsf 檔,
並在命令列模式(command line) 下執行 wscript 或 cscript 用 WSH (Window Script Host) 去跑程式即可,例如:
csript xxx.wsf
要注意的是,因為我們將檔存成了 UTF-8 編碼格式,
所以在 .wsf 檔中必需在 xml 聲明 (XML declaration) 中標示 encoding="UTF-8",
例如:
<?xml version="1.0" encoding="UTF-8"?>
以下為程式碼範例,
建立了一個 uploadFile(filePath, uploadTo) 函式來傳送檔案到 uploadTo 指定的 url,
其中 "http://localhost:8080/uploadFile.do" 是接收 httpPost request 的 server,這裡不做討論,可以參考這篇文,"使用 Java 上傳檔案(發送 enctype=multipart/form-data 的 HttpPost")。
在 uploadFile() 函式中,也可以看到傳了一個中文參數的範例 (uploadData.AddForm)。

uploadFile.wsf:
<?xml version="1.0" encoding="UTF-8"?>
<package>
<job id="xxx">

<script language="VBScript">
<![CDATA[
 uploadFile "D:\未命名.png", "http://localhost:8080/uploadFile.do"
 
 ''''''''''''''''''''''''''''''''''''''''''''''''''''
 Function uploadFile(filePath, uploadTo)
  
  Dim uploadData
  Set uploadData = New XMLUpload
  
  uploadData.Charset = "utf-8" ' see Public Property Let Charset(ByVal strValue)
  uploadData.openWithUrl uploadTo
  
  uploadData.AddForm "param1", "中文參數"  
  uploadData.AddFile "uploadedFile", filePath
  
  Dim responseStr
  responseStr = uploadData.Upload()
  Set uploadData = Nothing
  
  uploadFile = responseStr
 End Function
 
 Class XMLUpload
  Private xmlHttp
  Private objTemp
  Private adTypeBinary, adTypeText
  Private strCharset, strBoundary

  Private Sub Class_Initialize()
   adTypeBinary = 1
   adTypeText = 2
   Set xmlHttp = CreateObject("Msxml2.XMLHTTP")
   Set objTemp = CreateObject("ADODB.Stream")
   objTemp.Type = adTypeBinary
   objTemp.Open
   strCharset = "utf-8"
   strBoundary = GetBoundary()
  End Sub

  Private Sub Class_Terminate()
   objTemp.Close
   Set objTemp = Nothing
   Set xmlHttp = Nothing
  End Sub  
  
  '設置上傳使用的字符集
  Public Property Let Charset(ByVal strValue)
   strCharset = strValue
  End Property
  
  Public Sub openWithUrl(ByVal urlStr)
   xmlHttp.Open "POST", urlStr, False
  End Sub

  '獲取自訂義的表單數據分界線
  Private Function GetBoundary()
   Dim ret(12)
   Dim table
   Dim i
   table = "abcdefghijklmnopqrstuvwxzy0123456789"
   Randomize
   For i = 0 To UBound(ret)
    ret(i) = Mid(table, Int(Rnd() * Len(table) + 1), 1)
   Next
   GetBoundary = "---------------------------" & Join(ret, Empty)
  End Function  

  '添加文本域的名稱和值
  Public Sub AddForm(ByVal strName, ByVal strValue)
   Dim tmp
   tmp = "\r\n--$1\r\nContent-Disposition: form-data; name=""$2""\r\n\r\n$3"
   tmp = Replace(tmp, "\r\n", vbCrLf)
   tmp = Replace(tmp, "$1", strBoundary)
   tmp = Replace(tmp, "$2", strName)
   tmp = Replace(tmp, "$3", strValue)
   objTemp.Write StringToBytes(tmp, strCharset)
  End Sub
  
  '指定字符集的字符串轉字節數組
  Public Function StringToBytes(ByVal strData, ByVal strCharset)
   Dim objFile
   Set objFile = CreateObject("ADODB.Stream")
   objFile.Type = adTypeText
   objFile.Charset = strCharset
   objFile.Open
   objFile.WriteText strData
   objFile.Position = 0
   objFile.Type = adTypeBinary
   If UCase(strCharset) = "UNICODE" Then
    objFile.Position = 2 'delete UNICODE BOM
   ElseIf UCase(strCharset) = "UTF-8" Then
    objFile.Position = 3 'delete UTF-8 BOM
   End If
   StringToBytes = objFile.Read(-1)
   objFile.Close
   Set objFile = Nothing
  End Function

  '設置文件域的名稱/文件名稱/文件MIME類型/文件路徑或文件字節數組
  Public Sub AddFile(ByVal strName, ByVal strFilePath)
   Dim tmp, strFileName, strFileType, strExt   
   
   With CreateObject("Scripting.FileSystemObject")
    If .FileExists(strFilePath) Then
     strFileName = .GetFileName(strFilePath)
     strExt = .GetExtensionName(strFilePath)
    End IF
   End With
   
   With CreateObject("Scripting.Dictionary")
    .Add "php", "application/x-php"
    .Add "vbs", "application/x-vbs"
    .Add "jpe", "image/jpeg"
    .Add "jpg", "image/jpeg"
    .Add "jpeg", "image/jpeg"
    .Add "gif", "image/gif"
    .Add "png", "image/png"
    .Add "bmp", "image/bmp"
    .Add "ico", "image/x-icon"
    .Add "svg", "image/svg+xml"
    .Add "svgz", "image/svg+xml"
    .Add "tif", "image/tiff"
    .Add "tiff", "image/tiff"
    .Add "pct", "image/x-pict"
    .Add "psd", "image/vnd.adobe.photoshop"
    .Add "aac", "audio/x-aac"
    .Add "aif", "audio/x-aiff"
    .Add "flac", "audio/x-flac"
    .Add "m4a", "audio/x-m4a"
    .Add "m4b", "audio/x-m4b"
    .Add "mid", "audio/midi"
    .Add "midi", "audio/midi"
    .Add "mp3", "audio/mpeg"
    .Add "mpa", "audio/mpeg"
    .Add "mpc", "audio/x-musepack"
    .Add "oga", "audio/ogg"
    .Add "ogg", "audio/ogg"
    .Add "ra", "audio/vnd.rn-realaudio"
    .Add "ram", "audio/vnd.rn-realaudio"
    .Add "snd", "audio/x-snd"
    .Add "wav", "audio/x-wav"
    .Add "wma", "audio/x-ms-wma"
    .Add "avi", "video/x-msvideo"
    .Add "divx", "video/divx"
    .Add "flv", "video/x-flv"
    .Add "m4v", "video/mp4"
    .Add "mkv", "video/x-matroska"
    .Add "mov", "video/quicktime"
    .Add "mp4", "video/mp4"
    .Add "mpeg", "video/mpeg"
    .Add "mpg", "video/mpeg"
    .Add "ogm", "application/ogg"
    .Add "ogv", "video/ogg"
    .Add "rm", "application/vnd.rn-realmedia"
    .Add "rmvb", "application/vnd.rn-realmedia-vbr"
    .Add "smil", "application/x-smil"
    .Add "webm", "video/webm"
    .Add "wmv", "video/x-ms-wmv"
    .Add "xvid", "video/x-msvideo"
    .Add "js", "application/javascript"
    .Add "xml", "text/xml"
    .Add "html", "text/html"
    .Add "css", "text/css"
    .Add "txt", "text/plain"
    .Add "py", "text/x-python"
    .Add "pdf", "application/pdf"
    .Add "xhtml", "application/xhtml+xml"
    .Add "zip", "application/x-zip-compressed, application/zip"
    .Add "rar", "application/x-rar-compressed"
    .Add "cmd", "application/cmd"
    .Add "bat", "application/x-bat, application/x-msdos-program"
    .Add "exe", "application/exe, application/x-ms-dos-executable"
    .Add "msi", "application/x-msi"
    .Add "bin", "application/x-binary"
    .Add "crt", "application/x-x509-ca-cert"
    .Add "crl", "application/x-pkcs7-crl"
    .Add "pfx", "application/x-pkcs12"
    .Add "p12", "application/x-pkcs12"
    .Add "odc", "application/vnd.oasis.opendocument.chart"
    .Add "odf", "application/vnd.oasis.opendocument.formula"
    .Add "odb", "application/vnd.oasis.opendocument.database"
    .Add "odg", "application/vnd.oasis.opendocument.graphics"
    .Add "odi", "application/vnd.oasis.opendocument.image"
    .Add "odp", "application/vnd.oasis.opendocument.presentation"
    .Add "ods", "application/vnd.oasis.opendocument.spreadsheet"
    .Add "odt", "application/vnd.oasis.opendocument.tex"
    .Add "docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
    .Add "dotx", "application/vnd.openxmlformats-officedocument.wordprocessingml.template"
    .Add "potx", "application/vnd.openxmlformats-officedocument.presentationml.template"
    .Add "ppsx", "application/vnd.openxmlformats-officedocument.presentationml.slideshow"
    .Add "pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation"
    .Add "xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
    .Add "xltx", "application/vnd.openxmlformats-officedocument.spreadsheetml.template"
    .Add "ppam", "application/vnd.ms-powerpoint.addin.macroEnabled.12"
    .Add "ppa", "application/vnd.ms-powerpoint"
    .Add "potm", "application/vnd.ms-powerpoint.template.macroEnabled.12"
    .Add "ppsm", "application/vnd.ms-powerpoint.slideshow.macroEnabled.12"
    .Add "xlsm", "application/vnd.ms-excel.sheet.macroEnabled.12"
    .Add "pptm", "application/vnd.ms-powerpoint.presentation.macroEnabled.12"
    .Add "dotm", "application/vnd.ms-word.template.macroEnabled.12"
    .Add "docm", "application/vnd.ms-word.document.macroEnabled.12"
    .Add "doc", "application/msword"
    .Add "dot", "application/msword"
    .Add "pps", "application/mspowerpoint"
    .Add "ppt", "application/mspowerpoint,application/powerpoint,application/vnd.ms-powerpoint,application/x-mspowerpoint"
    .Add "xls", "application/vnd.ms-excel"
    .Add "xlt", "application/vnd.ms-excel"

    strFileType = .Item(LCase(strExt))
   End With
   
   tmp = "\r\n--$1\r\nContent-Disposition: form-data; name=""$2""; filename=""$3""\r\nContent-Type: $4\r\n\r\n"
   tmp = Replace(tmp, "\r\n", vbCrLf)
   tmp = Replace(tmp, "$1", strBoundary)
   tmp = Replace(tmp, "$2", strName)
   tmp = Replace(tmp, "$3", strFileName)
   tmp = Replace(tmp, "$4", strFileType)
   
   objTemp.Write StringToBytes(tmp, strCharset)
   objTemp.Write GetFileBinary(strFilePath)
  End Sub
  
  '獲取文件內容的字節數組
  Private Function GetFileBinary(ByVal strPath)
   Dim objFile
   Set objFile = CreateObject("ADODB.Stream")
   objFile.Charset = strCharset
   objFile.Type = adTypeBinary
   objFile.Open   
   objFile.LoadFromFile strPath
   GetFileBinary = objFile.Read(-1)
   objFile.Close
   Set objFile = Nothing
  End Function
  
  Public Sub AddHeader(ByVal strName, ByVal strValue)
   xmlHttp.setRequestHeader strName, strValue
  End Sub
  
  '上傳到指定的URL,并返回服務器應答
  Public Function Upload()
   Call AddEnd   
   xmlHttp.setRequestHeader "Content-Type", "multipart/form-data; boundary=" & strBoundary
   'xmlHttp.setRequestHeader "Content-Length", objTemp.size   
   xmlHttp.Send objTemp
   Upload = xmlHttp.responseText
  End Function
  
  '設置multipart/form-data結束標記
  Private Sub AddEnd()
   Dim tmp
   tmp = "\r\n--$1--\r\n"
   tmp = Replace(tmp, "\r\n", vbCrLf)
   tmp = Replace(tmp, "$1", strBoundary)
   objTemp.Write StringToBytes(tmp, strCharset)
   objTemp.Position = 2
  End Sub
 End Class
]]>
</script>
</job>
</package>

參考資料:

  1. HTTP協議之multipart/form-data請求分析
  2. VBS模拟POST上传文件
  3. File updload in post form in VBS
  4. Issues running JScript or VBScript files with UTF-8 encoding thru Windows Script Host
  5. WSF - Windows Script File XML Format
  6. XML
  7. CDATA
  8. Call 语句

2021年12月15日 星期三

使用 Java 上傳檔案(發送 enctype=multipart/form-data 的 HttpPost)

這篇記綠下如何使用 Java 上傳檔案 (發送 enctype=multipart/form-data 的 HttpPost)。

先建立一個簡單的檔案接收伺服器以利驗證上傳功能的正確性。
再直接使用 Java 進行 enctype=multipart/form-data 的 HpptPost 上傳檔案,
順便再傳送一個文字參數測試文字參數的傳遞功能是否也正確。

以下直接上程式碼,使用 jdk11,Tomcat 9.0
首先是範例有用到的 Maven Lib Dependency 如下:
<dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>
    
    <!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
	<dependency>
	    <groupId>javax.servlet</groupId>
	    <artifactId>javax.servlet-api</artifactId>
	    <version>4.0.1</version>
	    <scope>provided</scope>
	</dependency>
	
	<!-- https://mvnrepository.com/artifact/javax.servlet.jsp/javax.servlet.jsp-api -->
	<dependency>
	    <groupId>javax.servlet.jsp</groupId>
	    <artifactId>javax.servlet.jsp-api</artifactId>
	    <version>2.3.3</version>
	    <scope>provided</scope>
	</dependency>
	
	<!-- https://mvnrepository.com/artifact/javax.servlet.jsp.jstl/jstl-api -->
	<dependency>
	    <groupId>javax.servlet.jsp.jstl</groupId>
	    <artifactId>jstl-api</artifactId>
	    <version>1.2</version>
	</dependency>
 
	<!-- https://mvnrepository.com/artifact/org.apache.httpcomponents/httpclient -->
	<dependency>
	    <groupId>org.apache.httpcomponents</groupId>
	    <artifactId>httpclient</artifactId>
	    <version>4.5.13</version>
	</dependency>
	
	<!-- https://mvnrepository.com/artifact/org.apache.httpcomponents/httpmime -->
	<dependency>
	    <groupId>org.apache.httpcomponents</groupId>
	    <artifactId>httpmime</artifactId>
	    <version>4.5.13</version>
	</dependency>
	
	<!-- https://mvnrepository.com/artifact/commons-httpclient/commons-httpclient -->
	<!-- not maintain anymore, can be replaced by org.apache.httpcomponents -->
	<dependency>
	    <groupId>commons-httpclient</groupId>
	    <artifactId>commons-httpclient</artifactId>
	    <version>3.0.1</version>
	</dependency>
		
	<!-- https://mvnrepository.com/artifact/com.jfinal/cos -->
	<dependency>
	    <groupId>com.jfinal</groupId>
	    <artifactId>cos</artifactId>
	    <version>2020.4</version>
	</dependency>
	
  </dependencies>
接著建立一個簡單的 Servlet 來接收檔案 httpPost:
UploadFileAction.java:
import java.io.File;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.oreilly.servlet.MultipartRequest;
import com.oreilly.servlet.multipart.DefaultFileRenamePolicy;

/**
 * Servlet implementation class UploadFileAction
 */
@WebServlet("/uploadFile.do")
public class UploadFileAction extends HttpServlet {
	private static final long serialVersionUID = 1L;

    /**
     * Default constructor. 
     */
    public UploadFileAction() {
        //
    }

	/**
	 * @see HttpServlet#service(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		String dirSaveFilePath = "D:\\tempUploadedFile";
		if(!new File(dirSaveFilePath).exists()) {
			new File(dirSaveFilePath).mkdirs();
		}		
		MultipartRequest multipartRequest = new MultipartRequest(request, dirSaveFilePath, 100 * 1024 * 1024, "UTF-8", new DefaultFileRenamePolicy());
		System.out.println("ContentType: " + multipartRequest.getContentType("uploadedFile"));
		System.out.println("OriginalFileName: " + multipartRequest.getOriginalFileName("uploadedFile"));
		System.out.println("FileSystemName: " + multipartRequest.getFilesystemName("uploadedFile"));
		System.out.println("param1: " + multipartRequest.getParameter("param1"));
	}

	/**
	 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		service(request, response);
	}

	/**
	 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		service(request, response);
	}

}



最後是會進行檔案上傳的一個很單純的 Java,
這邊我演示了兩個不同的檔案上傳方式,
uploadFile_1():
使用了 org.apache.httpcomponents (較新,取代 commons-httpclient) 的 CloseableHttpClient 和 PostMethod

uploadFile_2():
使用了 commons-httpclient (已不再維護,由 commons-httpclient 取代) 的 HttpClient 和 PostMethod

org.apache.httpcomponents 和 commons-httpclient 的關係可參考
The Commons HttpClient project is now end of life, and is no longer being developed. It has been replaced by the Apache HttpComponents project in its HttpClient and HttpCore modules, which offer better performance and more flexibility.


UploadFileTestjava:
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URLConnection;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;

import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.multipart.FilePart;
import org.apache.commons.httpclient.methods.multipart.MultipartRequestEntity;
import org.apache.commons.httpclient.methods.multipart.Part;
import org.apache.commons.httpclient.methods.multipart.StringPart;
import org.apache.commons.httpclient.util.EncodingUtil;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.mime.HttpMultipartMode;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.entity.mime.content.StringBody;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.protocol.HTTP;
import org.apache.http.util.EntityUtils;

public class UploadFileTest {

	public static void main(String[] args) throws FileNotFoundException, IOException {
		uploadFile_1("C:\\Users\\Hugo\\Pictures\\未命名.png", "http://localhost:8080/uploadFile.do");
		uploadFile_2("C:\\Users\\Hugo\\Pictures\\未命名.png", "http://localhost:8080/uploadFile.do");
	}

	public static void uploadFile_1(String filePath, String uploadTo) throws FileNotFoundException, IOException {
		File file = new File(filePath);
		HttpPost httpPost = new HttpPost(uploadTo);

		MultipartEntityBuilder multipartEntityBuilder = MultipartEntityBuilder.create();
		//Use RFC6532 mode to avoid encoding Mojibake (garbled text) of fileName
		//使用 RFC6532 來避免中文等的檔案名稱在傳遞後變成亂碼
		multipartEntityBuilder.setMode(HttpMultipartMode.RFC6532); 
		
		multipartEntityBuilder.addPart("uploadedFile", new FileBody(file, ContentType.create(URLConnection.guessContentTypeFromName(file.getName())) , file.getName()));
//		multipartEntityBuilder.addPart("uploadedFile", new FileBody(file, ContentType.APPLICATION_OCTET_STREAM, file.getName()));		
//		multipartEntityBuilder.addBinaryBody("uploadedFile", file, ContentType.APPLICATION_OCTET_STREAM, file.getName());
				
		multipartEntityBuilder.addPart("param1", new StringBody("中文", ContentType.TEXT_PLAIN.withCharset(StandardCharsets.UTF_8)));
//		multipartEntityBuilder.addTextBody("param1", "中文", ContentType.TEXT_PLAIN.withCharset(StandardCharsets.UTF_8));

		HttpEntity httpEntity = multipartEntityBuilder.build();
		httpPost.setEntity(httpEntity);

		try (CloseableHttpClient httpClient = HttpClients.createDefault();
			) {
				CloseableHttpResponse httpResponse = httpClient.execute(httpPost);
				String responseStr = EntityUtils.toString(httpResponse.getEntity());
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public static void uploadFile_2(String filePath, String uploadTo) {
		File f = new File(filePath);
		PostMethod filePost = new PostMethod(uploadTo);
		filePost.getParams().setContentCharset("UTF-8");
//		filePost.addRequestHeader("xx", "xxx"); // if you want to add any header

		try {
			Part[] parts = { 
					  new StringPart("param1", "中文", "UTF-8")
					, new FilePart("uploadedFile", f) {
							@Override
							protected void sendDispositionHeader(OutputStream out) throws IOException {
								// override 掉sendDispositionHeader 方法以解決中文 fileName 傳送後會變成亂碼的問題
								// 在原始碼中是用 getAsciiBytes(fileName),我們這裡改用utf-8 去 encode
								// super.sendDispositionHeader(out) is overridden, use UTF-8 instead of Ascii to
								// encode filename
								
								// run code copied form Part.sendDispositionHeader() directly
								out.write(CONTENT_DISPOSITION_BYTES);
								out.write(QUOTE_BYTES);
								out.write(EncodingUtil.getAsciiBytes(getName()));
								out.write(QUOTE_BYTES);
				
								String filename = getSource().getFileName();
								if (filename != null) {
									out.write(EncodingUtil.getAsciiBytes(FILE_NAME));
									out.write(QUOTE_BYTES);
									out.write(EncodingUtil.getBytes(filename, "utf-8"));
									out.write(QUOTE_BYTES);
								}
							}
					} 
			};
			filePost.setRequestEntity(new MultipartRequestEntity(parts, filePost.getParams()));
			HttpClient client = new HttpClient();
			int status = client.executeMethod(filePost);
			String responseStr = filePost.getResponseBodyAsString();
		}catch(IOException e) {
			e.printStackTrace();
		}
	}

}
題外話:
  1. 因為用 Eclipse 的 Maven 外掛產生出的 JAVA EE 專案 (Archetype 選 maven-archetype-webapp) 是用舊版的 servlet 2.3,如果要用新版例如 servlet 3.0, 記得去 web.xml 裡把 <web-app> 裡的設定值改對,servlet 3.0 的設定值可參考這裡, 或是直接把 web.xml 刪掉也可以,因為 web.xml 對 servlet 3.0 並不是必需的。
  2. 因為 Eclipse 可能以為還是使用舊版 servlet 的關係,所以可能無法使用 Eclipse 裡面的相關功能, 例如無法用 Tomcat 啟動專案, 這時需要去修改專案的 Project Facets 設定,將 Dynamic Web Module 設成 3.0 (或以上版本),但有可能會發現無法修改, 這時可以去專案的 .setting 資料夾下找到 org.eclipse.wst.common.project.facet.core.xml, 將裡面的 facet="jst.web" verson 改成 3.0 即可,例如:
    <?xml version="1.0" encoding="UTF-8"?>
    <faceted-project>
      <fixed facet="wst.jsdt.web"/>
      <installed facet="jst.web" version="3.0"/>
      <installed facet="wst.jsdt.web" version="1.0"/>
      <installed facet="java" version="11"/>
    </faceted-project>
參考資料:
  1. HTTPClient PostMethod 中文乱码问题解决方案(2种)
  2. HTML <form> 标签的 enctype 属性
  3. Eclipse創建Maven-Web項目及解決 jre版本和web.xml版本問題
  4. Multipart Upload with HttpClient 4
  5. Getting a File’s Mime Type in Java
  6. Eclipse| 修改dynamic web module 为3.0版本
原始碼分享: