I am new to winHttp / client server communication, and stuck with sending a png image to server as attachment . I created an exe using C++ just to solve this problem . Somehow I managed to hit the server , and it is giving below response ,
{"code":"9999","message":"org.springframework.web.multipart.support.MissingServletRequestPartException: Required request part 'attachment' is not present","cause":".","correlationID":"***-***-43f4-82b2-***","apiURL":"POST : /file/4856.png","origin":"PartMfgServer","timestamp":1704814609769}
here is my BE code ,
@PostMapping(path = "/{fileName}", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public String uploadFileFromServer(@RequestHeader("File-Type") String contentType,
@PathVariable("fileName") String fileName,
@RequestParam(value = "fileRole", required = false) FILE_ROLE fileRole,
@RequestParam("attachment") MultipartFile fileToUpload)
{.......}
here is my code ,
int main() {
LPCWSTR server = L"domain.com";
LPCWSTR path = L"api/v2307/file/4856.png";
HINTERNET hSession = WinHttpOpen(L"ImageUploader/1.0", WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0);
if (!hSession) {
return -1;
}
HINTERNET hConnect = WinHttpConnect(hSession, server, INTERNET_DEFAULT_HTTPS_PORT, 0);
if (!hConnect) {
std::cerr << "WinHttpConnect failed!" << std::endl;
WinHttpCloseHandle(hSession);
return -1;
}
HINTERNET hRequest = WinHttpOpenRequest(hConnect, L"POST", path, NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, WINHTTP_FLAG_SECURE);
if (!hRequest) {
// Handle connection error
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
return -1;
}
// Open image file for reading
HANDLE hFile = CreateFile(L"C:\\Users\\m5zmfk\\AppData\\Local\\Temp\\nxOpen\\partfiles\\4856\\4856.png", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE) {
// Handle file open error
WinHttpCloseHandle(hRequest);
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
return -1;
}
DWORD fileSize = GetFileSize(hFile, NULL);
std::wstring boundary = L"---------------------------1234567890";
// std::wstring endBoundary = L"\r\n--" + boundary + L"--\r\n";
std::wstring formDataBody = L"--" + boundary + L"\r\n"
L"Content-Disposition: form-data; name=\"attachment\"; filename=\"4856.png\"\r\n"
L"\r\n--" + boundary + L"--\r\n";
std::wstring headers = L"authorization: ****\r\n"
L"Content-Type: multipart/form-data; boundary=" + boundary + L"\r\n"
L"File-Type: image/png\r\n"
L"Content-Length: " + std::to_wstring(fileSize + formDataBody.length()) +L"\r\n";
bool success = WinHttpAddRequestHeaders(hRequest, headers.c_str(), headers.length(), WINHTTP_ADDREQ_FLAG_ADD);
if (!success) {
std::cout << "Error adding headers" << std::endl;
printLastError(GetLastError());
}
DWORD timeout = 60000 * 2; // Set timeout to 60 seconds (adjust as needed)
WinHttpSetOption(hRequest, WINHTTP_OPTION_RECEIVE_TIMEOUT, &timeout, sizeof(timeout));
WinHttpSetOption(hRequest, WINHTTP_OPTION_SEND_TIMEOUT, &timeout, sizeof(timeout));
if (!WinHttpSendRequest(hRequest, WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 0, 0, 0)) {
// Handle request send error
printLastError(GetLastError());
CloseHandle(hFile);
WinHttpCloseHandle(hRequest);
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
return -1;
}
DWORD bytesWritten;
// Build the form-data request body
success = WinHttpWriteData(hRequest, formDataBody.c_str(), formDataBody.length(), &bytesWritten);
if (!success) {
printLastError(GetLastError());
CloseHandle(hFile);
WinHttpCloseHandle(hRequest);
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
return -1;
}
// Write the file content
BYTE buffer[1024];
DWORD bytesRead;
while (ReadFile(hFile, buffer, sizeof(buffer), &bytesRead, NULL) && bytesRead > 0) {
success = WinHttpWriteData(hRequest, buffer, bytesRead, &bytesWritten);
if (!success) {
printLastError(GetLastError());
CloseHandle(hFile);
WinHttpCloseHandle(hRequest);
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
return -1;
}
}
if (!WinHttpReceiveResponse(hRequest, NULL)) {
// Handle response error
printLastError(GetLastError());
CloseHandle(hFile);
WinHttpCloseHandle(hRequest);
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
return -1;
}
// Read and print the response
DWORD resRead;
BYTE resBuffer[1024];
while (WinHttpReadData(hRequest, resBuffer, sizeof(resBuffer), &resRead) && resRead > 0) {
// Process the received data (you can print it or save it to a file, etc.)
std::cout.write(reinterpret_cast<const char*>(resBuffer), resRead);
}
// Close the file and WinHTTP handles
CloseHandle(hFile);
WinHttpCloseHandle(hRequest);
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
return 0;
}
Can anyone help me with what is going wrong .
I tried giving formDataBody
as header using WinHttpAddRequestHeaders
. I got error code 87.
std::wstring formDataBody = L"--" + boundary + L"\r\n"
L"Content-Disposition: form-data; name=\"attachment\"; filename=\"4856.png\"\r\n"
L"\r\n--" + boundary + L"--\r\n";
You are not sending the PNG data correctly. It needs to be inside your formDataBody
variable, as the body of the attachment
MIME part, after its headers and before its closing boundary. You are sending the PNG data after all of the MIME data has been sent, which is wrong.
Also, MIME is based on 7bit ASCII, so you should not be using a std::wstring
for your formDataBody
variable, use std::string
instead (using std::wstring
for all of the other WinHTTP function parameters is fine).
Also, File-Type
is not a standard HTTP header. You should instead specify a standard Content-Type
header inside the attachment
MIME part, and then have your uploadFileFromServer()
handler retrieve the file type from that header (you can get it from the MultipartFile.getContentType()
method).
With that said, try something more like this:
bool readAll(HANDLE hFile, void *buffer, DWORD bufsize) {
char *ptr = static_cast<char*>(buffer);
DWORD bytesRead;
while (bufsize != 0) {
if (!ReadFile(hFile, ptr, bufsize, &bytesRead, NULL))
return false;
ptr += bytesRead;
bufsize -= bytesRead;
}
return true;
}
void printFailure(const char *funcName) {
DWORD error = GetLastError();
std::cerr << funcName << " failed!" << std::endl;
printLastError(error);
}
int main() {
LPCWSTR server = L"amn.dm-labs.com";
LPCWSTR path = L"api/v2307/file/4856.png";
HANDLE hFile = CreateFile(L"C:\\Users\\m5zmfk\\AppData\\Local\\Temp\\nxOpen\\partfiles\\4856\\4856.png", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE) {
printFailure("CreateFile");
return -1;
}
DWORD fileSize = GetFileSize(hFile, NULL);
if (fileSize == ERROR_INVALID_FILE_SIZE) {
printFailure("GetFileSize");
CloseHandle(hFile);
return -1;
}
std::string fileData;
if (fileSize > 0) {
fileData.resize(fileSize);
if (!readAll(hFile, &fileData[0], fileSize)) {
printFailure("ReadFile");
CloseHandle(hFile);
return -1;
}
}
CloseHandle(hFile);
HINTERNET hSession = WinHttpOpen(L"ImageUploader/1.0", WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0);
if (!hSession) {
printFailure("WinHttpOpen");
return -1;
}
HINTERNET hConnect = WinHttpConnect(hSession, server, INTERNET_DEFAULT_HTTPS_PORT, 0);
if (!hConnect) {
printFailure("WinHttpConnect");
WinHttpCloseHandle(hSession);
return -1;
}
HINTERNET hRequest = WinHttpOpenRequest(hConnect, L"POST", path, NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, WINHTTP_FLAG_SECURE);
if (!hRequest) {
printFailure("WinHttpOpenRequest");
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
return -1;
}
std::string sBoundary = "---------------------------1234567890";
std::wstring wBoundary = L"---------------------------1234567890";
std::string formDataBody = "--" + sBoundary + "\r\n"
"Content-Disposition: form-data; name=\"attachment\"; filename=\"4856.png\"\r\n"
"Content-Type: image/png\r\n"
"\r\n" +
sFileData + "\r\n"
"--" + sBoundary + "--\r\n";
std::wstring headers = L"authorization: ****\r\n"
L"Content-Type: multipart/form-data; boundary=" + wBoundary + L"\r\n";
DWORD timeout = 60000 * 2; // Set timeout to 60 seconds (adjust as needed)
WinHttpSetOption(hRequest, WINHTTP_OPTION_RECEIVE_TIMEOUT, &timeout, sizeof(timeout));
WinHttpSetOption(hRequest, WINHTTP_OPTION_SEND_TIMEOUT, &timeout, sizeof(timeout));
if (!WinHttpSendRequest(hRequest, headers.c_str(), headers.length(), formDataBody.c_str(), formDataBody.length(), formDataBody.length(), 0)) {
printFailure("WinHttpSendRequest");
WinHttpCloseHandle(hRequest);
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
return -1;
}
if (!WinHttpReceiveResponse(hRequest, NULL)) {
printFailure("WinHttpReceiveResponse");
WinHttpCloseHandle(hRequest);
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
return -1;
}
char resBuffer[1024];
DWORD resRead;
do {
if (!WinHttpReadData(hRequest, resBuffer, sizeof(resBuffer), &resRead)) {
printFailure("WinHttpReadData");
WinHttpCloseHandle(hRequest);
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
return -1;
}
if (resRead == 0)
break;
std::cout.write(resBuffer, resRead);
}
while (true);
WinHttpCloseHandle(hRequest);
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
return 0;
}
@PostMapping(path = "/{fileName}", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public String uploadFileFromServer(@PathVariable("fileName") String fileName,
@RequestParam(value = "fileRole", required = false) FILE_ROLE fileRole,
@RequestParam("attachment") MultipartFile fileToUpload)
{
String contentType = fileToUpload.getContentType();
...
}
If you really want to use WinHttpWriteData()
to send the PNG data in chunks, then it needs to look more like this instead:
void printFailure(const char *funcName) {
DWORD error = GetLastError();
std::cerr << funcName << " failed!" << std::endl;
printLastError(error);
}
int main() {
LPCWSTR server = L"amn.dm-labs.com";
LPCWSTR path = L"api/v2307/file/4856.png";
HANDLE hFile = CreateFile(L"C:\\Users\\m5zmfk\\AppData\\Local\\Temp\\nxOpen\\partfiles\\4856\\4856.png", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE) {
printFailure("CreateFile");
return -1;
}
DWORD fileSize = GetFileSize(hFile, NULL);
if (fileSize == ERROR_INVALID_FILE_SIZE) {
printFailure("GetFileSize");
CloseHandle(hFile);
return -1;
}
HINTERNET hSession = WinHttpOpen(L"ImageUploader/1.0", WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0);
if (!hSession) {
printFailure("WinHttpOpen");
CloseHandle(hFile);
return -1;
}
HINTERNET hConnect = WinHttpConnect(hSession, server, INTERNET_DEFAULT_HTTPS_PORT, 0);
if (!hConnect) {
printFailure("WinHttpConnect");
WinHttpCloseHandle(hSession);
CloseHandle(hFile);
return -1;
}
HINTERNET hRequest = WinHttpOpenRequest(hConnect, L"POST", path, NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, WINHTTP_FLAG_SECURE);
if (!hRequest) {
printFailure("WinHttpOpenRequest");
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
CloseHandle(hFile);
return -1;
}
std::string sBoundary = "---------------------------1234567890";
std::wstring wBoundary = L"---------------------------1234567890";
std::string formDataBody1 = "--" + sBoundary + "\r\n"
"Content-Disposition: form-data; name=\"attachment\"; filename=\"4856.png\"\r\n"
"Content-Type: image/png\r\n"
"\r\n";
std::string formDataBody2 = "\r\n"
"--" + sBoundary + "--\r\n";
DWORD totalLength = formDataBody1.length() + fileSize + formDataBody2.length();
DWORD bytesRead, bytesWritten;
std::wstring headers = L"authorization: ****\r\n"
L"Content-Type: multipart/form-data; boundary=" + wBoundary + L"\r\n";
DWORD timeout = 60000 * 2; // Set timeout to 60 seconds (adjust as needed)
WinHttpSetOption(hRequest, WINHTTP_OPTION_RECEIVE_TIMEOUT, &timeout, sizeof(timeout));
WinHttpSetOption(hRequest, WINHTTP_OPTION_SEND_TIMEOUT, &timeout, sizeof(timeout));
if (!WinHttpSendRequest(hRequest, headers.c_str(), headers.length(), WINHTTP_NO_REQUEST_DATA, 0, totalLength, 0)) {
printFailure("WinHttpSendRequest");
WinHttpCloseHandle(hRequest);
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
CloseHandle(hFile);
return -1;
}
if (!WinHttpWriteData(hRequest, formDataBody1.c_str(), formDataBody1.length(), &bytesWritten)) {
printFailure("WinHttpWriteData");
WinHttpCloseHandle(hRequest);
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
CloseHandle(hFile);
return -1;
}
if (fileSize > 0) {
BYTE buffer[1024];
do {
if (!ReadFile(hFile, buffer, sizeof(buffer), &bytesRead, NULL)) {
printFailure("ReadFile");
WinHttpCloseHandle(hRequest);
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
CloseHandle(hFile);
return -1;
}
if (bytesRead == 0)
break;
if (!WinHttpWriteData(hRequest, buffer, bytesRead, &bytesWritten)) {
printFailure("WinHttpWriteData");
WinHttpCloseHandle(hRequest);
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
CloseHandle(hFile);
return -1;
}
}
while (true);
}
CloseHandle(hFile);
if (!WinHttpWriteData(hRequest, formDataBody2.c_str(), formDataBody2.length(), &bytesWritten)) {
printFailure("WinHttpWriteData");
WinHttpCloseHandle(hRequest);
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
return -1;
}
if (!WinHttpReceiveResponse(hRequest, NULL)) {
printFailure("WinHttpReceiveResponse");
WinHttpCloseHandle(hRequest);
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
return -1;
}
char resBuffer[1024];
do {
if (!WinHttpReadData(hRequest, resBuffer, sizeof(resBuffer), &bytesRead)) {
printFailure("WinHttpReadData");
WinHttpCloseHandle(hRequest);
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
return -1;
}
if (bytesRead == 0)
break;
std::cout.write(resBuffer, bytesRead);
}
while (true);
WinHttpCloseHandle(hRequest);
WinHttpCloseHandle(hConnect);
WinHttpCloseHandle(hSession);
return 0;
}