QHttpMultiPart upload file boundary issue - qt

I'm trying to upload some files to a server by using QHttpMultiPart. The following version file is just an example. For some reason, Qt will automatically add boundary into the files. However, what I truly uploaded are .tar.gz files and they will be considered damaged if such boundary were added.
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
QNetworkAccessManager *netManager = new QNetworkAccessManager();
HTTPFirmwareUpgrader upgrader(netManager,"http://mytest.com");
upgrader.upgradeCIU();
return app.exec();
}
void HTTPFirmwareUpgrader::upgradeCIU()
{
QString filename = "version";
QString filePath = QString("C:/Users/User/Desktop/HTTP/%1").arg(filename);
qDebug() << uploadFirmware(filename, filePath);
}
bool HTTPFirmwareUpgrader::uploadFirmware(const QString &filename, const QString &filePath)
{
QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
QHttpPart myPart;
QFile *file = new QFile(filePath);
if(!file->exists())
qWarning() << "File DOES NOT exists";
file->open(QIODevice::ReadOnly);
myPart.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("form-data"));
myPart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"version\"; filename=\"version\""));
myPart.setBodyDevice(file);
file->setParent(multiPart);
multiPart->append(ciu);
QByteArray resp = this->post(createUploadRequest(QString("filename=%1").arg(filename)),
multiPart, file);
qDebug() << "Upload Filrmware " << resp;
return resp != "";
}
QByteArray HTTPFirmwareUpgrader::post(QUrl url, QHttpMultiPart *multiPart, QFile *file)
{
QNetworkRequest request;
QEventLoop loop;
request.setUrl(url);
request.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("multipart/form-data"));
QNetworkReply *reply = m_manager->post(request, multiPart);
connect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
loop.exec();
multiPart->setParent(reply);
reply->deleteLater();
return reply->readAll();
}
This is the original "version" file:
enter image description here
And this is the "version" file that transferred:
enter image description here
And if I do not set header for the request, the size of the transferred file will be 0.
Does anyone have any idea what should I do?

Problem solved!!!
Turns out I use the wrong way to upload the file. What I need to use is QIODevice instead of QMultipart.

Related

Save and load QWebEngineHistory to a QWebEnginePage

I need to save the history of a QWebEnginePage and load it back. Therefore I want to store the history from page A in some structure and set it to page B.
In the documentation I found the following methods:
// Saves the web engine history history into stream.
QDataStream &operator<<(QDataStream &stream, const QWebEngineHistory &history)
// Loads the web engine history from stream into history.
QDataStream &operator>>(QDataStream &stream, QWebEngineHistory &history)
But honestly I don't know how to work with them.
I tried the following:
QWebEnginePage *m_history;
...
...
void setHistory(QWebEngineHistory *history){
QDataStream data;
data << history; //Hoping that the content of data is persistent after deleting of the QWebEnginePage where the history is coming from
data >> m_history;
}
And later on I want to load it back to the page:
m_history >> m_webEnginePage.history(); // Pseudo-Code
I know that the QWebEngineHistory of a QWebEnginePage is const, but then I'm wondering why are there even those two methods from above? Why is there a function that "loads the web engine history into history"?
The only alternative I can think of is storing my history in a QList, but managing this is not nice and could lead to more problems (because of the whole forward/backward button etc).
Thank you very much for your help.
No object can be saved, what is saved is the information associated with the object so you should not create QWebEngineHistory but save and/or load the information.
In the following example, the information is saved in a file when the application is closed and the startup is loaded.
#include <QtWebEngineWidgets>
int main(int argc, char *argv[]) {
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QApplication app(argc,argv);
const QString filename = "history.bin";
QWebEngineView view;
view.load(QUrl("https://stackoverflow.com"));
{// load
QFile file(filename);
if(file.open(QFile::ReadOnly)){
qDebug() << "load";
QDataStream ds(&file);
ds >> *(view.page()->history());
}
}
view.resize(640, 480);
view.show();
int ret = app.exec();
{// save
QFile file(filename);
if(file.open(QFile::WriteOnly)){
qDebug() << "save";
QDataStream ds(&file);
ds << *(view.page()->history());
}
}
return ret;
}
In the same way you can save it through QSettings:
#include <QtWebEngineWidgets>
int main(int argc, char *argv[]) {
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QApplication app(argc,argv);
QWebEngineView view;
view.load(QUrl("https://stackoverflow.com"));
{// load
QSettings settings;
QByteArray ba = settings.value("page/history").toByteArray();
QDataStream ds(&ba, QIODevice::ReadOnly);
ds >> *(view.page()->history());
}
view.resize(640, 480);
view.show();
int ret = app.exec();
{// save
QSettings settings;
QByteArray ba;
QDataStream ds(&ba, QIODevice::WriteOnly);
ds << *(view.page()->history());
settings.setValue("page/history", ba);
}
return ret;
}

How to save Mimedata to a file

I have been trying to serialize my tree structure and restore it back with not much of success.
Mimedata function is able to store the tree structure in the correct hierarchy.
How can i save the mime data to a file and load it back to QDataStream ?
QMimeData *TreeModel::mimeData(const QModelIndexList &indexes) const
{
QMimeData *mimeData = new QMimeData;
QByteArray data;
QDataStream stream(&data, QIODevice::WriteOnly);
QList<TreeItem *> nodes;
foreach(const QModelIndex &index, indexes) {
TreeItem *node = getItem(index);
if (!nodes.contains(node))
nodes << node;
}
stream << QCoreApplication::applicationPid();
stream << nodes.count();
foreach(TreeItem *node, nodes) {
buildTree(node, stream);
}
mimeData->setData(s_treeNodeMimeType, data);
return mimeData;
}
You must implement the stream operators between QDataStream and QMimeData saving the formats and data associated with each format and the number of formats.
#include <QtWidgets>
QDataStream &operator<<(QDataStream &stream, const QMimeData &data)
{
const QStringList formats = data.formats();
stream << static_cast<qint32>(formats.size());
for(const QString& format : formats)
stream << format << data.data(format);
return stream;
}
QDataStream &operator>>(QDataStream &stream, QMimeData &data)
{
data.clear();
qint32 size;
QString format;
QByteArray formatData;
stream >> size;
while(size-->0){
stream >> format >> formatData;
data.setData(format,formatData);
}
return stream;
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
const QString text = "Text";
const QString html = R"(<!DOCTYPE html>
<html>
<body>
<h1>My First Heading</h1>
<p>My first paragraph.</p>
</body>
</html>
)";
const QList<QUrl> urls = {
{"https://stackoverflow.com/questions/58073079/how-to-save-mimedata-to-a-file"},
{"https://doc.qt.io/qt-5/"}
};
const QString mimeType = "custom/custom-app";
const QByteArray data("stackoverflow");
const QString path = "datastream.dat";
{
QMimeData mimedata;
mimedata.setText(text);
mimedata.setHtml(html);
mimedata.setUrls(urls);
mimedata.setData(mimeType, data);
QFile file(path);
file.open(QIODevice::WriteOnly);
QDataStream out(&file);
out << mimedata;
}
{
QMimeData mimedata;
QFile file(path);
file.open(QIODevice::ReadOnly);
QDataStream out(&file);
out >> mimedata;
Q_ASSERT(mimedata.text() == text);
Q_ASSERT(mimedata.html() == html);
Q_ASSERT(mimedata.urls() == urls);
Q_ASSERT(mimedata.data(mimeType) == data);
}
return 0;
}

Uploading a file in Multipart form data in Qt5

I am trying desperately to upload a file to a server in Multipart. I have almost the same code as in the Qt docs, but the file isn't uploading to the server.
Here's what I have in my Debug:
---------Uploaded-------------- 3672 of 3672
---------Uploaded-------------- 3672 of 3672
---------Uploaded-------------- 3672 of 3672
---------Uploaded-------------- 0 of 0
----------Finished--------------
"Error transferring http://MyUrlHere.com/uploadFile - server replied: Bad Request" 400 QNetworkReplyHttpImpl(0x17589ff0)
The problem is not coming from the server because when I try uploading a file on it in multipart with a Chrome or Firefox extention it actually works!
Here is my code:
QUrl testUrl("http://MyUrlHere.com/uploadFile ");
QNetworkRequest request(testUrl);
QNetworkProxy proxy;
proxy.setType(QNetworkProxy::HttpProxy);
proxy.setHostName("proxy");
proxy.setPort(8080);
QNetworkProxy::setApplicationProxy(proxy);
QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
QString preview_path = "C:/Users/Desktop/image.jpg";
QHttpPart previewPathPart;
previewPathPart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"preview_path\""));
previewPathPart.setBody(preview_path.toLatin1());
QString preview_name = "image.jpg";
QHttpPart previewFilePart;
previewFilePart.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("image/jpeg"));
previewFilePart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"preview_file\"; filename=\""+ preview_name + "\""));
QFile *file = new QFile(preview_path);
file->open(QIODevice::ReadOnly);
previewFilePart.setBodyDevice(file);
file->setParent(multiPart); // we cannot delete the file now, so delete it with the multiPart
multiPart->append(previewPathPart);
multiPart->append(previewFilePart);
QNetworkAccessManager *networkManager= new QNetworkAccessManager;
reply = networkManager->post(request, multiPart);
multiPart->setParent(reply); // delete the multiPart with the reply
connect(reply, SIGNAL(finished()),
this, SLOT (uploadDone()));
connect(reply, SIGNAL(uploadProgress(qint64, qint64)),
this, SLOT (uploadProgress(qint64, qint64)));
}
void ApkDialog::uploadProgress(qint64 bytesSent, qint64 bytesTotal) {
qDebug() << "---------Uploaded--------------" << bytesSent<< "of" <<bytesTotal;
}
void ApkDialog::uploadDone() {
qDebug() << "----------Finished--------------" << reply->errorString() <<reply->attribute( QNetworkRequest::HttpStatusCodeAttribute).toInt();
qDebug()<<reply;
// reply->deleteLater();
}
I found the error. It was a request error. there is a little thing missing in Qt docs.
Here is my code that is working :
QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
QHttpPart imagePart;
//imagePart.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("text/plain"));
imagePart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"file\"; filename=\"version.txt\""));/* version.tkt is the name on my Disk of the file that I want to upload */
QHttpPart textPart;
textPart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"name\""));
textPart.setBody("toto");/* toto is the name I give to my file in the server */
QString apkLocation = apktextEdit->text();
QFile *file = new QFile(apkLocation);
file->open(QIODevice::ReadOnly);
imagePart.setBodyDevice(file);
file->setParent(multiPart); // we cannot delete the file now, so delete it with the multiPart
multiPart->append(textPart);
multiPart->append(imagePart);
QUrl url("http://MyUrl.com");
QNetworkRequest request(url);
QNetworkAccessManager *networkManager= new QNetworkAccessManager;
reply = networkManager->post(request, multiPart);
multiPart->setParent(reply); // delete the multiPart with the reply
connect(reply, SIGNAL(finished()),
this, SLOT (uploadDone()));
connect(reply, SIGNAL(uploadProgress(qint64, qint64)),
this, SLOT (uploadProgress(qint64, qint64)));
}
There is no need in filename=\"version.txt\" here:
imagePart.setHeader(QNetworkRequest::ContentDispositionHeader,
QVariant("form-data; name=\"file\"; filename=\"version.txt\""));
You set file name here:
QString apkLocation = apktextEdit->text();
And it works fine just with the following line:
imagePart.setHeader(QNetworkRequest::ContentDispositionHeader,
QVariant("form-data; name=\"file\""));
Anyway thanks a lot, it was really heplful!

Qt HTTP GET freezes screen

I'm writing a Qt program to get an image from site and insert in a QLabel. When I send my request my screen freezes and nothing more occurs.
Notice I'm new in Qt.
Based on my initial knowledge of Qt it's enough send a signal when download is finished.
...
MapReader::MapReader(QWidget *parent, Qt::WFlags flags)
: QMainWindow(parent, flags)
{
ui.setupUi(this);
imageLabelMap = ui.imageMap;
getImageButton = ui.getImageButton;
networkManager = new QNetworkAccessManager(this);
setup();
}
MapReader::~MapReader()
{
}
void MapReader::setup()
{
QObject::connect(getImageButton, SIGNAL(clicked()), this, SLOT(triggerDownload()));
QObject::connect(networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(finishedDownload(QNetworkReply*)));
}
void MapReader::setImage(QByteArray imageBytes)
{
QImage map;
...
}
void MapReader::triggerDownload()
{
QUrl url("http://images.tsn.ca/images/stories/2012/09/26/terrydunfield_2035-430x298.jpg");
QNetworkReply* reply = networkManager->get(QNetworkRequest(url));
QObject::connect(reply, SIGNAL(readyRead()), &loop, SLOT(quit()));
}
void MapReader::finishedDownload(QNetworkReply* reply)
{
reply->deleteLater();
QVariant statusCodeV = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
QVariant redirectionTargetUrl = reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
if(reply->error() != QNetworkReply::NoError)
{
QMessageBox msgBox;
msgBox.setWindowTitle("Error");
msgBox.setInformativeText("Error on downloading file: \n"+reply->errorString());
msgBox.exec();
return;
}
QVariant attribute = reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
if (attribute.isValid())
{
QUrl url = attribute.toUrl();
qDebug() << "must go to:" << url;
return;
}
setImage(reply->readAll());
}
I think there is some code missing that might give us a clue. You have
QObject::connect(reply, SIGNAL(readyRead()), &loop, SLOT(quit()));
But I don't see where loop is defined? Sounds like you are running an additional event loop?
Regardless, you don't need that. This should be as simple as:
void MapReader::triggerDownload()
{
QUrl url("http://images.tsn.ca/images/stories/2012/09/26/terrydunfield_2035-430x298.jpg");
QNetworkReply* reply = networkManager->get(QNetworkRequest(url));
QObject::connect(reply, SIGNAL(finished()), this, SLOT(finishedDownload()));
}
void MapReader::finishedDownload()
{
QNetworkReply *reply = qobject_cast<QNetworkReply *>(sender()); // sender() allows us to see who triggered this slot - in this case the QNetworkReply
QVariant statusCodeV = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
QVariant redirectionTargetUrl = reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
if(reply->error() != QNetworkReply::NoError)
{
QMessageBox msgBox;
msgBox.setWindowTitle("Error");
msgBox.setInformativeText("Error on downloading file: \n"+reply->errorString());
msgBox.exec();
return;
}
QVariant attribute = reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
if (attribute.isValid())
{
QUrl url = attribute.toUrl();
qDebug() << "must go to:" << url;
return;
}
setImage(reply->readAll());
reply->deleteLater();
}
Make sure you have defined finishedDownload() as a slot in your header file

Qt Imgur upload fail

I have an uploader like this
void uploader::upload() {
QUrl serviceUrl = QUrl("http://api.imgur.com/2/upload.json");
QByteArray postData;
postData.append("key=MY_KEY&");
postData.append("image=" + file.toBase64());
QNetworkAccessManager *networkManager = new QNetworkAccessManager(this);
connect(networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(serviceRequestFinished(QNetworkReply*)));
networkManager->post(QNetworkRequest(serviceUrl), postData);
}
void uploader::serviceRequestFinished(QNetworkReply* reply) {
qDebug() << "Done" << endl;
QString response = reply->readAll();
qDebug() << response;
QScriptValue sc;
QScriptEngine engine;
sc = engine.evaluate(response);
qDebug() << sc.property("upload").property("links").property("original").toString();
}
file is QByteArray with PNG file content which i want to upload.
When uploading ends, I receive the response, but uploaded image is not valid (if i save it and trying to open, it writes that the file is not valid PNG image).
What I'm doing wrong?

Resources