Welcome!

Welcome to the official BlackBerry Support Community Forums.

This is your resource to discuss support topics with your peers, and learn from each other.

inside custom component

Native Development

Reply
Developer
Algorithmus
Posts: 16
Registered: ‎09-17-2012
My Device: Developer
Accepted Solution

Problem uploading through http

I am creating a media app which allows users to access their files over a cloud server. One of the functions I want to provide is uploading files from the BB10 device to the cloud. The server accepts OAuth2 calls.

 

I am using a slightly modified version of the KQOAuth library, which you can find here: https://github.com/kylefowler/tumblr/tree/master/src/oauth

(I am mostly concerned with kqoauthmanager.cpp though)

And I know I am sending the request correctly because I did manage to send some files successfully. I wanted to know if this had anything to do with whether or not the files I was sending were plain text or binary files, or even the file size, but it seems to have no effect; I've had files that are only a few hundred KB in size fail, and even a 20MB mp3 succeeded (and it has whitespace characters in its filename too). And some of the files that fail aren't even corrupt. They open just fine on my computer, and the static web interface for the cloud service has no problems with uploading them.

An example request to POST a file upload might look like this:

 

Authorization: TAuth realm=[realm]; tauth_token=[token]
Content-Type: multipart/form-data; boundary=1599e8e129
--1599e8e129
Content-Disposition: form-data;name="control"
Content-Type: text/plain
 
{ "transactionId" : "181","filesize" : 15 }
--1599e8e129
Content-Disposition: form-data;name="file";filename="README.txt"
Content-Type: application/octet-stream;charset=UTF-8
Content-Transfer-Encoding: binary
 
[Binary contents]
--1599e8e129--

 

KQOAuth makes use of QNetworkAccessManager and QNetworkReply, and I'm loading the contents into a QBuffer (because I would prefer the * QIODevice parameter in QNetworkReply over the const QByteArray for the larger files).

 

I create the new KQOAuthRequest like this:

KQOAuth2Request *req = new KQOAuth2Request(this);
if (!checkToken()) {
if (!id.isEmpty()) {
req->initRequest(KQOAuthRequest::AuthorizedRequest, QUrl(urls->getMap()[UPLOAD].toString()+"/folder/"+id));
} else {
req->initRequest(KQOAuthRequest::AuthorizedRequest, QUrl(urls->getMap()[UPLOAD].toString()));
}
 
req->setToken(oauthSettings.value("access_token").toString());
req->setHttpMethod(KQOAuthRequest::POST);
req->setProperty("realm", "t-online.de");
req->setRequestOAuthMethod(KQOAuthRequest::OAUTH2);
QByteArray data;
QString boundary = QString::number(qrand()*(90000000000)/(RAND_MAX+1)+10000000000, 16);
 
QFile *fileData = new QFile(file);
QString filename = fileData->fileName();
 
req->setContentType("multipart/form-data; boundary="+boundary);
 
data.append(QString("--").toAscii()+boundary.toAscii());
data.append(QString("\r\nContent-Disposition: form-data;name=\"control\"\r\n").toAscii());
data.append(QString("Content-Type: text/plain\r\n\r\n").toAscii());
data.append(QString("{ \"transactionId\" : \"").toAscii());
if (oauthSettings.value("transactionId").isNull()) {
oauthSettings.setValue("transactionId", 1);
} else {
oauthSettings.setValue("transactionId", oauthSettings.value("transactionId").toInt()+1);
}
data.append(oauthSettings.value("transactionId").toString().toAscii());
data.append(QString("\",\"filesize\" : ").toAscii());
data.append(QString::number(fileData->size()).toAscii());
data.append(QString(" }\r\n").toAscii());
data.append(QString("--").toAscii()+boundary.toAscii());
data.append(QString("\r\nContent-Disposition: form-data;name=\"file\";filename=\"").toAscii());
data.append(filename.toAscii());
data.append(QString("\"\r\n").toAscii());
data.append(QString("Content-Type: application/octet-stream;charset=UTF-8\r\n").toAscii());
data.append(QString("Content-Transfer-Encoding: binary\r\n\r\n").toAscii());
 
req->setRawData(data);
req->setBoundary(boundary.toAscii());
req->setUploadUrl(file);
}
return req;

 

 And then in KQOAuthManager.cpp, the request gets sent like this (I modified the file a bit, but d is basically just a private kqoauthmanager object, which you can find under kqoauthmanager_p.h. I added the boundary and uploadUrl though.):

QFile *file = new QFile(request->getUploadUrl());
        file->open(QIODevice::ReadOnly);
        d->uploadData = new QBuffer();
                d->uploadData->open(QIODevice::ReadWrite);
                 d->uploadData->write(request->rawData()); //Header information
                d->uploadData->write(file->readAll()); //Binary file itself
                file->close();
                delete file;
                file = NULL;
                d->uploadData->write(QString("\r\n").toAscii()); //Ending boundary
                d->uploadData->write(QString("--").toAscii()+request->getBoundary().toAscii()+QString("--").toAscii());
                d->uploadData->seek(0);
         reply = d->networkManager->post(networkRequest, d->uploadData);

 

(I attempted to close and delete d->uploadData afterwards in the finished signal, as I found out from other threads that *QIODevice would not be deleted, and I have to do it myself, but for some reason, it's already gone before I manage to do it myself.) Anyways, I am wondering why some file uploads make it, and some don't, but I'm not very familiar with Qt, and I have no idea if QBuffer, QByteArray or QNetworkReply are properly managing the binary files. I get the 500 error on the files that fail, but I suspect it has something to do with the way I'm sending some binary files, even though every other resource I've found on the Internet says to put it in a QByteArray. I've also tried converting the data to base64, but that just fails everything. I looked all over the Internet, but nobody else seems to be having the same problem, or I'm not using the right keywords. If you have any idea why this is happening, any advice would be great. Thanks.

Please use plain text.
BlackBerry Development Advisor
lingBB10Dev
Posts: 35
Registered: ‎08-01-2012
My Device: BB10 alpha-BB10 developer
My Carrier: T-Mobile

Re: Problem uploading through http

Were u able to figure out what was the problem?

 

the followings are some thoughts:

 

1) since you're getting "HTTP Error 500 Internal server error" for files that fail,   do you have access for the server logs for more info?


2) Also, the QNetworkAccessManager::smileytongue:ost( const QNetworkRequest & request, QIODevice * data ) function says that "data must be open for reading and must remain valid until the finished() signal is emitted for this reply" , just a wild guess, is it possible tht that your codeinvalidate  d->uploadData before finished is emitted?

 

 

 

Please use plain text.
Developer
Algorithmus
Posts: 16
Registered: ‎09-17-2012
My Device: Developer

Re: Problem uploading through http

I fixed it. It turns out I had the stream on ReadWrite, which was probably what was causing uploadData to become corrupt. So now I use WriteOnly to write the binary file to the stream, then open it again with ReadOnly for QNetworkReply. I was a little worried about the extra call to open the stream twice wouldn't be a good idea, and that it might not work for the larger files, but it works anyways.

 

uploadData is still being deleted before the finished signal somehow though.

Please use plain text.