-
오토핫키와 Winhttp #6. multipart/form-data오토핫키/winhttp 2024. 7. 13. 07:17
압살맨 오픈채팅 바로가기
압살맨 유튜브 바로가기
시작
간만이죠? 일 그만두고 갓수의 생활을 즐기다가 5개월만에 다시 현생으로 복귀하면서 키보드도 다시 잡았습니다.
이 파트는 주로 파일을 업로드할 때 사용하는 정해진 규격? 형식? 인데 첨부파일이 포함된 이메일을 송부한다던가
특정 사이트에 이미지 PDF 등등을 업로드할 때 사용합니다. Base64 형식으로도 업로드 하는 사이트들이 있는데 Base64 는 원본보다 30% 정도 볼륨이 커지는 문제가 있어서 통신이 그만큼 느려지는 단점도 있고 리소스도 많이 먹어서 formdata 형식을 사용하는 사이트가 많이 있습니다.
본문
가장 먼저 우리가 업로드를 어디다가 진행하느냐를 정해야 한다.
무료 이미지 호스팅 / 이미지 업로드
이미지를 업로드 하고공유해보세요. 원하는 곳 어디든 끌어놓기로 이미지를 바로 업로드해보세요.(이미지당 32 MB 가능) 다이렉트 링크, BBCode 및 HTML 미리보기등을 제공해드립니다.
imgbb.com
imgbb
무료로 이미지를 업로드 하고 호스팅 할 수 있고 API 도 지원한다. (But, 지원하는 API 예시가 형편없어서 안 쓸 예정)
해당 사이트에 들어가서 개발자 도구(F12) - 네트워크 탭을 누르고 새로고침
그 이후 임의의 이미지를 업로드
개발자 도구 안에 보면
여기로 감
json 이라고 있고 페이로드 - 소스 보기
이런 데이터를 보낸 것을 알 수 있다.
이게 바로 우리가 지금 다룰 multipart/form-data 이다.
; 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" } }
해당 소스의 원본은
https://www.autohotkey.com/boards/viewtopic.php?f=6&t=7647
CreateFormData - Create "multipart/form-data" for http post (Updated:2019-01-13) - AutoHotkey Community
CreateFormData v1.30 (2019-01-13) Source Modified version by SKAN (doesn't require BinArr.ahk) Functions: CreateFormData(ByRef retData, ByRef retHeader, objParam) CreateFormData_WinInet(ByRef retData, ByRef retHeader, objParam) Used for VxE's HTTPRequest()
www.autohotkey.com
이 곳인데 원작자는 'tmplinshi' 이후 개선버전은 'SKAN' 에 의해 1차 개선
이후 'iseahound' 에 의해 최종적인 개선버전이 나왔다.
아무튼
------WebKitFormBoundary6RstoC3kHewqbkQF Content-Disposition: form-data; name="source"; filename="Range_20240710111318.png" Content-Type: image/png ------WebKitFormBoundary6RstoC3kHewqbkQF Content-Disposition: form-data; name="type" file ------WebKitFormBoundary6RstoC3kHewqbkQF Content-Disposition: form-data; name="action" upload ------WebKitFormBoundary6RstoC3kHewqbkQF Content-Disposition: form-data; name="timestamp" 1720820484981 ------WebKitFormBoundary6RstoC3kHewqbkQF Content-Disposition: form-data; name="auth_token" da8c488247626df44bb336a5255962ba8f01fcc5 ------WebKitFormBoundary6RstoC3kHewqbkQF--
여기 보면 파일이 업로드 되고
type 밑에 file
action 밑에 upload...가 있고
마지막에 auth_token 이 있다.
엥? auth_token? 이게 뭐노
네트워크 - 문서로 가면
imgbb.com 새로고침 할 때인데 여기서 응답으로 가서 Ctrl + F 를 누르고 auth_token 을 검색한다.
그럼 하단에 토큰값을 부여하는 script 가 있다는 것을 알 수 있다.
이제 예제를 만들어보자
#Include <CreateFormData> ;아까 폼데이터 만드는 소스를 include 해야함 #Include <JSON> ;추가로 JSON 형식의 Response 를 배열로 바꿔주는 소스 깃헙에서 구하자 wh := ComObjCreate("WinHttp.WinHttpRequest.5.1") ;Auth_Token 값 따는 부분 wh.Open("Get", "https://imgbb.com/") wh.Send() if(RegExMatch(wh.ResponseText, "auth_token=""(.*?)""", token)) auth_token := token1 else { Msgbox, Auth_Token Error ExitApp } ;폼데이터 생성 Obj := {"source" : [File] , "type" : "file" , "action" : "upload" , "timestamp" : Unix(10000) , "auth_token" : auth_token} CreateFormData(PD, CT, Obj) ;폼데이터 Post wh.Open("POST", "https://imgbb.com/json") wh.SetRequestHeader("Accept", "*/*") wh.SetRequestHeader("Content-Type", CT) wh.SetRequestHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36") wh.Send(PD) MyData := Json.Load(RegExReplace(wh.ResponseText,"\\")) Msgbox, % MyData.image.url ;UnixTimeStamp 를 만들어주는 함수 Unix(divide) { Static UnixStart := 116444736000000000 DllCall("GetSystemTimeAsFileTime", "Int64P", FileTime) Return (FileTime - UnixStart) // divide }
이러면 이제 해당 이미지 URL 을 받을 수 있다.
Obj := {"source" : [File] , "type" : "file" , "action" : "upload" , "timestamp" : Unix(10000) , "auth_token" : auth_token}
File 은 파일명이 들어가야 한다. 예를 들면 "C:\Users\OH_YS\Documents\ASM Capture\2024-07-12\Range_20240712135109.png" 혹은 같은 위치에 있다면 " Range_20240712135109.png" 이렇게 해주면 된다.
참고로 imgbb.com 는 무료일 때 용량 제한이 얼마인지 나와있지는 않지만 다른 블로거의 실험에 의하면 10GB 라고 한다.
결론
multipart/form-data 를 사용하는 사이트를 제어하는 것은 이제 ㅈㄴ 쉽다. ㅇㅈ?
이거 알면 웹 제어는 거의 다 아는거라고 봐도 무방하다.
아주 간혹 일반적으로 받을 수 없는 데이터를 넣어서 보내야 하는 경우도 있는데 이건 나도 몰?루
그냥 포기하는 방법을 사용함. 그럼 이만
'오토핫키 > winhttp' 카테고리의 다른 글
오토핫키와 Winhttp #5. Winhttp 와 Adodb Stream 으로 파일 다운로드 구현 (0) 2023.07.11 오토핫키와 Winhttp #4. 파라미터를 주소에 표시하지 않는 사이트 파싱하기 (0) 2023.02.22 오토핫키와 Winhttp #3. 원래 주소가 숨겨진 사이트를 파싱하는 방법 (0) 2023.02.16 오토핫키와 Winhttp #2. 파라미터가 주소에 입력되는 사이트 크롤링 (1) 2023.02.15 오토핫키와 Winhttp #1. winhttp 의 이해 (3) 2023.01.19