-
오토핫키와 Winhttp #6. multipart/form-data오토핫키/winhttp 2024. 7. 13. 07:17
압살맨 오픈채팅 바로가기
압살맨 유튜브 바로가기
시작
간만이죠? 일 그만두고 갓수의 생활을 즐기다가 5개월만에 다시 현생으로 복귀하면서 키보드도 다시 잡았습니다.
이 파트는 주로 파일을 업로드할 때 사용하는 정해진 규격? 형식? 인데 첨부파일이 포함된 이메일을 송부한다던가
특정 사이트에 이미지 PDF 등등을 업로드할 때 사용합니다. Base64 형식으로도 업로드 하는 사이트들이 있는데 Base64 는 원본보다 30% 정도 볼륨이 커지는 문제가 있어서 통신이 그만큼 느려지는 단점도 있고 리소스도 많이 먹어서 formdata 형식을 사용하는 사이트가 많이 있습니다.
본문
가장 먼저 우리가 업로드를 어디다가 진행하느냐를 정해야 한다.
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
이 곳인데 원작자는 '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