Thursday, March 17, 2005

Posting multipart/form-data using QHttp.

I am no QT expert to write articles on it, but just that I would like other to be save off troubles that I have had. If you find any flaws/corrections please write a comment here, I will update it ASAP.

After lots of struggle I finally got it working, Thanks to Rocky and Nirnimesh for their time and discussions. What follows is mostly generic but example is based on Content-Type of multipart/form-data. The same applies to application/x-www-form-urlencoded too.
Problem in sending Request
Problem in getting Response



I was initially using this NON Working code segment:

> QByteArray payload;
> QDataStream data_stream(payload, IO_WriteOnly | IO_Raw);
>
> data_stream << "--7d44e178b0434"<
> data_stream << "Content-Disposition: form-data; name=\"email\" "<
> data_stream <<>
> data_stream << "abs@bbc.com"<
>
> data_stream << "--7d44e178b0434"<
> data_stream << "Content-Disposition: form-data; name=\"password\"
> "<
> data_stream <<>
> data_stream << "mypassword"<
>
> data_stream << "--7d44e178b0434"<
> data_stream << "Content-Disposition: form-data; name=\"photo\";
> filename=\""<text()<
> <"\""<
> data_stream << "Content-Type: image/jpeg"<
> data_stream << "Content-Length: "<<
>
> data_stream <<>
> cout<<"Filename as it is sent is "<text()<
> int i=0;
> char *s=new char[1024];
> while(file.readBlock(s,1023)){
> data_stream.writeRawBytes(s,1023);
> }
> file.close();
>
> data_stream << "--7d44e178b0434--"<
> header.setValue("Keep-Alive","300");
> header.setValue("Connection","keep-alive");
> header.setContentLength(payload.size());
> cout<<"Payload size is "<
>
> this_buffer = new QBuffer();
> req_id=_http_client->request(header, payload,0);

I was not able to figure out the problem for long time, but when I used ethereal everything became clear, There was difference between the packets sent by me and the browser for the request. Ethereal showed the first few lines i.e before the file content were not written as expected:

^M
--5728459211650640751785373418^M
Content-Disposition: form-data; name="email"^M
^M
dfgfdgfdg^M

but were written in something like.
^@^@^@^@^P--5728459211650640751785373418^@^@^@^@^C^M
^@^@^@^@.Content-Disposition: form-data; name="email" ^@^@^@^@^C^M
^@^@^@^@^C^M
^@^@^@^@^[dfgfdgfdg^@^@^@^@^C^M
^@^@^@^@^P--5728459211650640751785373418^@^@^@^@^C^M

Now I realized that there was some problem with formatting of data sent to DataStream using "<<", We realized that using writeRawBytes solves all the problem. So here is the WORKING code sequence after correction.

QString boundary="7d44e178b0434";
QString endline="\r\n";
QString start_delim="--"+boundary+endline;
QString cont_disp_str="Content-Disposition: form-data; ";

QString email_str = start_delim + cont_disp_str + "name=" + "\"email\""+endline+endline+"vardhman.jain..."+endline;

data_stream.writeRawBytes(email_str.data(),email_str.length());

QString password_str = start_delim + cont_disp_str + "name=" + "\"password\""+endline+endline+"vardhman.jain..."+endline;
data_stream.writeRawBytes(password_str.data(),password_str.length());

QString path=le_filename->text();

QString only_filename = path.section( '/', -1 );

QString photo_str = start_delim + cont_disp_str + "name=" + "\"photo\""+"; filename="+"\""+only_filename+"\""
+endline+"Content-Type: image/jpeg"+endline+endline;
data_stream.writeRawBytes(photo_str.data(),photo_str.length());


long int i=0;
char *s=new char[file.size()+2];
while((i=file.readBlock(s,file.size()))){//I know it is not good to do so, but just to speed up.
data_stream.writeRawBytes(s,i);

}
file.close();

QString stop_delim=endline+"--"+boundary+"--"+endline;
//data_stream << "--7d44e178b0434--"<
data_stream.writeRawBytes(stop_delim.data(),stop_delim.length());
req_id=_http_client->request(header, payload,0);

Solution to some other problems you might face are:
1) If you get 400 Invalid Request Format
i)Check that the line POST 'filename' has a filename which is relative to / I mean / is very important.
Either give it a full URL or the filename relative to WWW Document root starting with /
ii) Check the presence of "\r\n" at the end of each line.
iii)Use ethereal to debug the packet content in details.
(To be completed over time)

Following links have been of some use to me.
http://www.mime-rpc.com/examples.html
Info about Multipart/Form-data format.

Problems in READING response
I don't know what problem can occur here, but some ppl have posted there doubts regarding the same, so I put the code that works for me.

void postData::readyRead( const QHttpResponseHeader & resp){
cout<<"Number of bytes readable"<<_http_client->bytesAvailable()<<>
long temp;
QByteArray ba;
ba=_http_client->readAll();
QString temp1=ba;
cout<< "Bytes read"<<>
}

This is connected to the slot readyRead as follows:

connect(_http_client, SIGNAL(readyRead(const QHttpResponseHeader&)), this,
SLOT(readyRead(const QHttpResponseHeader&)));



4 comments:

  1. This comment has been removed by a blog administrator.

    ReplyDelete
  2. Thanks google for giving some credit to my .. but I hardly did anything... It was you only who found the problem, incidently the pc was mine and I was sitting beside you :)

    ReplyDelete
  3. I'm a newbie to Qt and I'm trying to mimic your code presented on the blog to do a POST of multipart/form-data with no luck so far. Is that possible for you to help me out here if I show you the code on what I'm doing.

    Thanks in advance.

    ReplyDelete
  4. Would be difficult for me to help you debug that thing, it takes time to read your code and understand it etc.

    You should consider using live http header plugin by mozila to see the packets being sent.

    Also consider using Ethereal (this is what I used, if the packet is well formed Ethereal will be able to show it to you in proper format).

    ReplyDelete