CreateFormData - Create "multipart/form-data" for http post (Updated:2019-01-13)

Post your working scripts, libraries and tools for AHK v1.1 and older
blue83
Posts: 157
Joined: 11 Apr 2018, 06:38

Re: CreateFormData - Create "multipart/form-data" for http post (Updated:2019-01-13)

24 Dec 2021, 08:06

Hi,

When I put this code it works great.

Code: Select all

objParam := { files : ["test1.pdf","test2.pdf"] }
But if I put this code then I have a problem, can you help me?

Code: Select all

file1 := "test1.pdf"
file2 := "test2.pdf"
newvar := file1 . "," . file2
objParam := { files : [newvar] }
malcev
Posts: 1769
Joined: 12 Aug 2014, 12:37

Re: CreateFormData - Create "multipart/form-data" for http post (Updated:2019-01-13)

24 Dec 2021, 16:48

Code: Select all

file1 := "test1.pdf"
file2 := "test2.pdf"

a := [file1,file2]
msgbox % a[1]

newvar := file1 . "," . file2
a := [newvar]
msgbox % a[1]
iseahound
Posts: 1459
Joined: 13 Aug 2016, 21:04
Contact:

Re: CreateFormData - Create "multipart/form-data" for http post (Updated:2019-01-13)

04 Sep 2022, 13:50

Fresh rewrite for September 2022.

SKAN's code doesn't make a distinction between a handle and a pointer to memory (one is movable, and the other isn't) so it was possible* to crash the script. In addition, the excessive reallocations, and keeping track of the size of the buffer are all unnecessary if you use a stream interface. Also, I think SKAN linked to his own personal site which is now down (?) but I changed the example to file.io and the example should work for the foreseeable future.

The StrPutUTF8 code is ugly and can be eliminated by changing the ptr to an astr in IStream_Write but that would remove support for Unicode characters in the API call.

* Generally, not possible because Microsoft encounters many people who mix up handles and pointers, so they have taken measures to work with that code to prevent crashes.

Code: Select all

; CreateFormData() by tmplinshi, AHK Topic: https://autohotkey.com/boards/viewtopic.php?t=7647
; Thanks to Coco: https://autohotkey.com/boards/viewtopic.php?p=41731#p41731
; Modified version by SKAN, 09/May/2016
; Rewritten by iseahound in September 2022

CreateFormData(ByRef retData, ByRef retHeader, objParam) {
    New CreateFormData(retData, retHeader, objParam)
}

Class CreateFormData {

    __New(ByRef retData, ByRef retHeader, objParam) {

        Local CRLF := "`r`n", i, k, v, str, pvData
        ; Create a random Boundary
        Local Boundary := this.RandomBoundary()
        Local BoundaryLine := "------------------------------" . Boundary

        ; Create an IStream backed with movable memory.
        hData := DllCall("GlobalAlloc", "uint", 0x2, "uptr", 0, "ptr")
        DllCall("ole32\CreateStreamOnHGlobal", "ptr", hData, "int", False, "ptr*", pStream:=0, "uint")
        this.pStream := pStream

        ; Loop input paramters
        For k, v in objParam
        {
            If IsObject(v) {
                For i, FileName in v
                {
                    str := BoundaryLine . CRLF
                        . "Content-Disposition: form-data; name=""" . k . """; filename=""" . FileName . """" . CRLF
                        . "Content-Type: " . this.MimeType(FileName) . CRLF . CRLF

                    this.StrPutUTF8( str )
                    this.LoadFromFile( Filename )
                    this.StrPutUTF8( CRLF )

                }
            } Else {
                str := BoundaryLine . CRLF
                    . "Content-Disposition: form-data; name=""" . k """" . CRLF . CRLF
                    . v . CRLF
                this.StrPutUTF8( str )
            }
        }

        this.StrPutUTF8( BoundaryLine . "--" . CRLF )

        this.pStream := ObjRelease(pStream) ; Should be 0.
        pData := DllCall("GlobalLock", "ptr", hData, "ptr")
        size := DllCall("GlobalSize", "ptr", pData, "uptr")

        ; Create a bytearray and copy data in to it.
        retData := ComObjArray( 0x11, size ) ; Create SAFEARRAY = VT_ARRAY|VT_UI1
        pvData  := NumGet( ComObjValue( retData ), 8 + A_PtrSize , "ptr" )
        DllCall( "RtlMoveMemory", "Ptr", pvData, "Ptr", pData, "Ptr", size )

        DllCall("GlobalUnlock", "ptr", hData)
        DllCall("GlobalFree", "Ptr", hData, "Ptr")                   ; free global memory

        retHeader := "multipart/form-data; boundary=----------------------------" . Boundary
    }

    StrPutUTF8( str ) {
        length := StrPut(str, "UTF-8") - 1 ; remove null terminator
        VarSetCapacity(utf8, length)
        StrPut(str, &utf8, length, "UTF-8")
        DllCall("shlwapi\IStream_Write", "ptr", this.pStream, "ptr", &utf8, "uint", length, "uint")
    }

    LoadFromFile( filepath ) {
        DllCall("shlwapi\SHCreateStreamOnFileEx"
                    ,   "wstr", filepath
                    ,   "uint", 0x0             ; STGM_READ
                    ,   "uint", 0x80            ; FILE_ATTRIBUTE_NORMAL
                    ,    "int", False            ; fCreate is ignored when STGM_CREATE is set.
                    ,    "ptr", 0               ; pstmTemplate (reserved)
                    ,   "ptr*", pFileStream:=0
                    ,   "uint")
        DllCall("shlwapi\IStream_Size", "ptr", pFileStream, "uint64*", size:=0, "uint")
        DllCall("shlwapi\IStream_Copy", "ptr", pFileStream , "ptr", this.pStream, "uint", size, "uint")
        ObjRelease(pFileStream)
    }

    RandomBoundary() {
        str := "0|1|2|3|4|5|6|7|8|9|a|b|c|d|e|f|g|h|i|j|k|l|m|n|o|p|q|r|s|t|u|v|w|x|y|z"
        Sort, str, D| Random
        str := StrReplace(str, "|")
        Return SubStr(str, 1, 12)
    }

    MimeType(FileName) {
        n := FileOpen(FileName, "r").ReadUInt()
        Return (n        = 0x474E5089) ? "image/png"
            :  (n        = 0x38464947) ? "image/gif"
            :  (n&0xFFFF = 0x4D42    ) ? "image/bmp"
            :  (n&0xFFFF = 0xD8FF    ) ? "image/jpeg"
            :  (n&0xFFFF = 0x4949    ) ? "image/tiff"
            :  (n&0xFFFF = 0x4D4D    ) ? "image/tiff"
            :  "application/octet-stream"
    }

}


; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
; Working example
; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -


whr := ComObjCreate("WinHttp.WinHttpRequest.5.1")
whr.Open("POST", "https://file.io", true)

objParam := { maxDownloads  : "1"
            , autoDelete    : "true"
            , file          : ["a.jpg"] } ; Change filepath with an existing image.

CreateFormData(PostData, hdr_ContentType, objParam)

whr.SetRequestHeader("Content-Type", hdr_ContentType)
whr.SetRequestHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko")
whr.Option(6) := False ; No auto redirect
whr.Send(PostData)
whr.WaitForResponse()
MsgBox % whr.ResponseText
Run % RegExReplace(whr.ResponseText, "^.*?(https://file.io/.+?)" Chr(34) ".*$", "$1")
User avatar
RaptorX
Posts: 390
Joined: 06 Dec 2014, 14:27
Contact:

Re: CreateFormData - Create "multipart/form-data" for http post (Updated:2019-01-13)

19 Jan 2023, 13:47

Converted it to Autohotkey v2

Code: Select all

; CreateFormData() by tmplinshi, AHK Topic: https://autohotkey.com/boards/viewtopic.php?t=7647
; Thanks to Coco: https://autohotkey.com/boards/viewtopic.php?p=41731#p41731
; Modified version by SKAN, 09/May/2016
; Rewritten by iseahound in September 2022
; Converted to v2 by RaptorX 19/01/2023

Class CreateFormData {

    __New(&retData, &retHeader, objParam) {

        Local CRLF := "`r`n", i, k, v, str, pvData
        ; Create a random Boundary
        Local Boundary := CreateFormData.RandomBoundary()
        Local BoundaryLine := "------------------------------" . Boundary

        ; Create an IStream backed with movable memory.
        hData := DllCall("GlobalAlloc", "uint", 0x2, "uptr", 0, "ptr")
        DllCall("ole32\CreateStreamOnHGlobal", "ptr", hData, "int", False, "ptr*", &pStream:=0, "uint")
        CreateFormData.pStream := pStream

        ; Loop input paramters
        For k, v in objParam.OwnProps()
        {
            If IsObject(v) {
                For i, FileName in v
                {
                    str := BoundaryLine . CRLF
                        . 'Content-Disposition: form-data; name="' . k . '"; filename="' . FileName . '"' . CRLF
                        . 'Content-Type: ' . CreateFormData.MimeType(FileName) . CRLF . CRLF

                    CreateFormData.StrPutUTF8( str )
                    CreateFormData.LoadFromFile( Filename )
                    CreateFormData.StrPutUTF8( CRLF )

                }
            } Else {
                str := BoundaryLine . CRLF
                    . 'Content-Disposition: form-data; name="' . k '"' . CRLF . CRLF
                    . v . CRLF
                CreateFormData.StrPutUTF8( str )
            }
        }

        CreateFormData.StrPutUTF8( BoundaryLine . "--" . CRLF )

        CreateFormData.pStream := ObjRelease(pStream) ; Should be 0.
        pData := DllCall("GlobalLock", "ptr", hData, "ptr")
        size := DllCall("GlobalSize", "ptr", pData, "uptr")

        ; Create a bytearray and copy data in to it.
        retData := ComObjArray( 0x11, size ) ; Create SAFEARRAY = VT_ARRAY|VT_UI1
        pvData  := NumGet( ComObjValue( retData ), 8 + A_PtrSize , "ptr" )
        DllCall( "RtlMoveMemory", "Ptr", pvData, "Ptr", pData, "Ptr", size )

        DllCall("GlobalUnlock", "ptr", hData)
        DllCall("GlobalFree", "Ptr", hData, "Ptr")                   ; free global memory

        retHeader := "multipart/form-data; boundary=----------------------------" . Boundary
    }

    static StrPutUTF8( str ) {
        buf := Buffer(StrPut(str, "UTF-8") - 1) ; remove null terminator
        StrPut(str, buf, buf.size, "UTF-8")
        DllCall("shlwapi\IStream_Write", "ptr", CreateFormData.pStream, "ptr", buf.Ptr, "uint", buf.Size, "uint")
    }

    static LoadFromFile( filepath ) {
        DllCall("shlwapi\SHCreateStreamOnFileEx"
                    ,   "wstr", filepath
                    ,   "uint", 0x0             ; STGM_READ
                    ,   "uint", 0x80            ; FILE_ATTRIBUTE_NORMAL
                    ,    "int", False            ; fCreate is ignored when STGM_CREATE is set.
                    ,    "ptr", 0               ; pstmTemplate (reserved)
                    ,   "ptr*", &pFileStream:=0
                    ,   "uint")
        DllCall("shlwapi\IStream_Size", "ptr", pFileStream, "uint64*", &size:=0, "uint")
        DllCall("shlwapi\IStream_Copy", "ptr", pFileStream , "ptr", CreateFormData.pStream, "uint", size, "uint")
        ObjRelease(pFileStream)
    }

    static RandomBoundary() {
        str := "0|1|2|3|4|5|6|7|8|9|a|b|c|d|e|f|g|h|i|j|k|l|m|n|o|p|q|r|s|t|u|v|w|x|y|z"
        Sort str, 'D| Random'
        str := StrReplace(str, "|")
        Return SubStr(str, 1, 12)
    }

    static MimeType(FileName) {
        n := FileOpen(FileName, "r").ReadUInt()
        Return (n        = 0x474E5089) ? "image/png"
            :  (n        = 0x38464947) ? "image/gif"
            :  (n&0xFFFF = 0x4D42    ) ? "image/bmp"
            :  (n&0xFFFF = 0xD8FF    ) ? "image/jpeg"
            :  (n&0xFFFF = 0x4949    ) ? "image/tiff"
            :  (n&0xFFFF = 0x4D4D    ) ? "image/tiff"
            :  "application/octet-stream"
    }

}

; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
; Working example
; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -


whr := ComObject("WinHttp.WinHttpRequest.5.1")
whr.Open("POST", "https://file.io", true)

objParam := { maxDownloads  : "1"
            , autoDelete    : "true"
            , file          : ["D:\Cloud\RaptorX\OneDrive\Desktop\simple training.json"] } ; Change filepath with an existing image.

CreateFormData(&PostData, &hdr_ContentType, objParam)

whr.SetRequestHeader("Content-Type", hdr_ContentType)
whr.SetRequestHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko")
; whr.Option(6) := False ; No auto redirect
whr.Send(PostData)
whr.WaitForResponse()
MsgBox whr.ResponseText
Run RegExReplace(whr.ResponseText, "^.*?(https://file.io/.+?)" Chr(34) ".*$", "$1")
Projects:
AHK-ToolKit
marceldeeno
Posts: 9
Joined: 03 Jun 2022, 03:13

Re: CreateFormData - Create "multipart/form-data" for http post (Updated:2019-01-13)

06 Apr 2023, 12:10

Thanks to everyone who worked on this script. It still works on the same share addresses.
Now I'm doing a bit of research myself, but I can't get it to work on my own website, probably due to different parameters?
This is my website upload test address https://tst.webcentre.nl/1/uploader.php

Code: Select all

whr := ComObject("WinHttp.WinHttpRequest.5.1")
whr.Open("POST", "https://tst.webcentre.nl/1/uploader.php", true)

objParam := { maxDownloads  : "1"
            , autoDelete    : "true"
            , file          : ["D:\Cloud\RaptorX\OneDrive\Desktop\simple training.json"] } ; Change filepath with an existing image.

CreateFormData(&PostData, &hdr_ContentType, objParam)

whr.SetRequestHeader("Content-Type", hdr_ContentType)
whr.SetRequestHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko")
; whr.Option(6) := False ; No auto redirect
whr.Send(PostData)
whr.WaitForResponse()
MsgBox whr.ResponseText
Run RegExReplace(whr.ResponseText, "^.*?(https://tst.webcentre.nl/1/uploader.php/.+?)" Chr(34) ".*$", "$1")
Now it's not so much about the return code but a piece of technology, maybe I need to adjust the PHP code?
Can someone help me in this?
malcev
Posts: 1769
Joined: 12 Aug 2014, 12:37

Re: CreateFormData - Create "multipart/form-data" for http post (Updated:2019-01-13)

11 Apr 2023, 06:24

marceldeeno, if You want to use ahk v.2 then You need to ask in ahk v.2 subforum.
Here We discuss about code of ahk v.1.

Code: Select all

objParam := {fileToUpload: ["G:\ezgif-2-c343d06f3719.jpg"]}
CreateFormData(postData, hdr_ContentType, objParam)

whr := ComObjCreate("WinHttp.WinHttpRequest.5.1")
whr.Open("POST", "https://tst.webcentre.nl/1/uploader.php", true)
whr.SetRequestHeader("Content-Type", hdr_ContentType)
whr.SetRequestHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko")
whr.Send(PostData)
whr.WaitForResponse()
MsgBox % whr.ResponseText

Return to “Scripts and Functions (v1)”

Who is online

Users browsing this forum: No registered users and 36 guests