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 语句

沒有留言 :

張貼留言