Hi Neil,
Glad to help. First, on your webpage that will upload the binary file, place a form with a file upload/selection object in it like this:
<form name="image_form" enctype="multipart/form-data" method="post" target="_blank" action="http:recv_image.cgi" onsubmit="upld_img_btn()"> <input title="Click to choose a file" type="file" id="img_file" name="img_file" /> <input type="button" value="Upload New Image" onclick="upld_img_btn()" /></form>
The action field "http:recv_image" specifies the name of the cgi function that will be called by the MQX httpd server on my project to receive and process the uploaded file. You can refer to the HVAC demo for examples of how to specify which cgi functions are called.
I also used a clientside Javascript function to actually perform the file upload from the webpage like this:
function upld_img_btn() { var filepath_str = document.getElementById("img_file").value if(filepath_str == "") { alert("No File Selected. Please Select A Firmware Image File First."); return; } var parsed = filepath_str.split( "\\" ); var iCount = parsed.length-1; var filename_only = parsed[iCount]; var sPromptString = "WARNING - About To Upload System Firmware Image File[ " + filename_only + " ] Please Confirm Or Cancel"; var retval=confirm(sPromptString); if(retval!=true) return; document.forms["image_form"].submit(); //Submit the image upload form data to the cgi function on your server return; }
Here's a stripped down version of the function running on my MQX project that is called by the MQX httpd server when the file is uploaded by the user. In my actual function, I use MQX functions to store the received file as a boot image in the image table and set it as active for the next reboot.
_mqx_int cgi_recv_image(HTTPD_SESSION_STRUCT *session) { boolean bMore2Come; uint_8 cByte; uint_16 iMsgIdStrngLength, iBoundaryLength; _mqx_int lReadLength, lTotalRecvdMsgBytes, lBytesRead, lFileByteSize, lCount, lTemp, lWriteSize, lRetval = 0; char_ptr sRecvBuffer = NULL; char_ptr sBufferPtr = NULL; char_ptr sBufferDataStartPtr = NULL; char_ptr sBufferEndPtr = NULL; char_ptr sTmpStrng = NULL; char sFileNameStrng[gcMAX_MSGSTRNGSIZE+1];//Allocate some temp buffers//---------------------------------------------------------------------------- lTotalRecvdMsgBytes = session->request.content_len; if(lTotalRecvdMsgBytes == 0) return(0); sRecvBuffer = _mem_alloc(lTotalRecvdMsgBytes); if(sRecvBuffer == NULL) { //... Handle error here return(0); } sTmpStrng = _mem_alloc(gc2K_STRING); //String needs to be large enough to store the http POST reply page if(sTmpStrng == NULL) { //... Handle error here return(0); }//Read in the data until all bytes (per lTotalRecvdMsgBytes value) are received//---------------------------------------------------------------------------- sBufferPtr = sRecvBuffer; //Use sBufferPtr as a movable temp pointer so sRecvBuffer can always point to buffer start lReadLength = httpd_read(session, sBufferPtr, lTotalRecvdMsgBytes); //Get any waiting bytes lBytesRead = lReadLength; //Record how many bytes we extracted from the incoming stream session->request.content_len -= lReadLength; while(session->request.content_len > 0) //Keep reading the incoming stream until all data bytes spec'd { //in session->request.content_len have been received sBufferPtr += lReadLength; //Advance buffer pointer to location where next bytes are to be stored lReadLength = httpd_read(session, sBufferPtr, (lTotalRecvdMsgBytes - lBytesRead)); //Retreive waiting bytes from incoming stream session->request.content_len -= lReadLength; lBytesRead += lReadLength; }//Example of header info that leads the http POST data sent by the webpage:////-----------------------------BoundaryString\r\nContent-Disposition: Form data: name="img_file"; filename="xxxxxxxx.xxx"\r\nContent-Type:application/octet stream\r\n\r\n//|_____________iBoundaryLength_____________| |variable| | variable | --Actual Start Of Data Payload Here->/// length length //Count the number of chars in the leading http encapsulation boundary string as per the above example://---------------------------------------------------------------------------- iBoundaryLength = 0; sBufferPtr = sRecvBuffer; //Reset sBufferPtr to start of sRecvBuffer while(*sBufferPtr != '\r') //Count the number of chars in the boundary string { //(String is finished at the \r\n chars) sBufferPtr++; iBoundaryLength++; }//Calculate the number of bytes of the file data payload in the received string//---------------------------------------------------------------------------- sBufferPtr = NULL; sBufferPtr = (char *)strstr(sRecvBuffer, "Content-Type"); //Find the start of the actual data which follows the Content-Type string if(sBufferPtr == NULL) { //... Handle error here _mem_free(sRecvBuffer); _mem_free(sTmpStrng); return(lRetval); } while(*sBufferPtr != '\r') //Count the number of chars in the Content-Type string sBufferPtr++; //(string is finished at the \r\n\r\n chars) sBufferPtr += 4; //Advance sBufferPtr past the \r\n\r\n chars to the actual start of the file data sBufferDataStartPtr = sBufferPtr; //Remember this location for later use lTemp = sBufferPtr - sRecvBuffer; //Determine how many chars preceeded the start of data lFileByteSize = lTotalRecvdMsgBytes - lTemp; //File size is total recvd bytes minus leading http header bytes lFileByteSize -= (iBoundaryLength + 6);//and trailing http closing boundary( "\r\n-----------------------------BoundaryString--\r\n" -END- )// |_____________iBoundaryLength_____________|//Do what you need to with the data//---------------------------------------------------------------------------- sBufferEndPtr = sBufferPtr + lFileByteSize; while(sBufferPtr < sBufferEndPtr) { }//Issue the all ok reply//---------------------------------------------------------------------------- sprintf(sTmpStrng, "Received %d Byte Firmware Image File[ %s ]\n", lFileByteSize, sFileNameStrng); strcat(sTmpStrng, "<br/><br/>\n"); strcat(sTmpStrng, "This new firmware image has been stored into flash and recorded in the Image Table\n"); strcat(sTmpStrng, "<br/>\n"); strcat(sTmpStrng, "as the currently active firmware image. When the device is restarted this new image\n"); strcat(sTmpStrng, "<br/>\n"); strcat(sTmpStrng, "will be loaded into memory and started. If a different boot image image is desired \n"); strcat(sTmpStrng, "<br/>\n"); strcat(sTmpStrng, "you may select that image from the Router Firmware Control webpage.\n"); lRetval = cgi_SendResponseWindow(session, sTmpStrng);//Clean up and exit happy here//---------------------------------------------------------------------------- _mem_free(sRecvBuffer); _mem_free(sTmpStrng); return(lRetval); }
Here's the cgi function that sends my reply to the web page. Since I added the "target="_blank" tag to my form, this reply will be displayed in a new window on the client computer.
_mqx_int cgi_SendResponseWindow(HTTPD_SESSION_STRUCT *session, char_ptr spString) { session->response.contenttype = CONTENT_TYPE_HTML; httpd_sendhdr(session, 0, 0); httpd_sendstr(session->sock, "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n"); httpd_sendstr(session->sock, "<html>\n"); httpd_sendstr(session->sock, "<head>\n"); httpd_sendstr(session->sock, "<title>Firmware Image Upload Response</title>\n"); httpd_sendstr(session->sock, "<script type=\"text/javascript\">\n"); httpd_sendstr(session->sock, "function closeWin()\n"); httpd_sendstr(session->sock, " {\n"); httpd_sendstr(session->sock, " window.close();\n"); httpd_sendstr(session->sock, " return;\n"); httpd_sendstr(session->sock, " }\n"); httpd_sendstr(session->sock, "</script>\n"); httpd_sendstr(session->sock, "</head>\n"); httpd_sendstr(session->sock, "<body>\n"); httpd_sendstr(session->sock, "<br/><br/>\n"); httpd_sendstr(session->sock, spString); httpd_sendstr(session->sock, "<br/><br/>\n"); httpd_sendstr(session->sock, "<br/><br/>\n"); httpd_sendstr(session->sock, "<input type=\"button\" value=\"Close This Window\" onclick=\"closeWin()\" />\n"); httpd_sendstr(session->sock, "</body>\n"); httpd_sendstr(session->sock, "</html>\n"); return(session->request.content_len); }
I am pretty far from being an expert in this area so use this code at your own risk. It seems to work pretty well for me. Hope this helps.
Best Regards,
Tim