[Qt4.1] Dateiupload mit QHttp

Code-Schnippsel, oder Tipps und Tricks, die einem beim Programmieren mit Qt helfen können.
Antworten
macman
Beiträge: 1738
Registriert: 15. Juni 2005 13:33
Wohnort: Gütersloh
Kontaktdaten:

[Qt4.1] Dateiupload mit QHttp

Beitrag von macman »

Da öfter danach gefragt wurde, und es anscheinend nicht einfach ist :-), stelle ich hier mal ein Programm zum Dateiupload zur Verfügung. Kompiliert und getestet habe ich es nur mit Qt 4.1.3. Es handelt sich hierbei um ein Konsolenprogramm, mit dem man eine oder mehrere Dateien an ein PHP-Skript senden kann. Aufruf ist wie folgt:

programm -u=http://server/transfer.php -f=file1 -f=file2 [-p=serverantwort.html]

Zum testen packe ich mein PHP-Skript gleich bei.

Transfer.php

Code: Alles auswählen

<?php
$path="./";
$log = fopen("request.log", "w");
fwrite($log, "Anfrage angekommen\r\n");

$uploadlog = array();
if ($_POST)
{
	fwrite($log, "POST angekommen\r\n");

	$count = 0;
	if (isset($_FILES['datei']))
		$count = count($_FILES['datei']['name']);
	for ($i=0; $i<$count; $i++)
		if (move_uploaded_file($_FILES['datei']['tmp_name'][$i], $path.$_FILES['datei']['name'][$i]))
			array_push($uploadlog, "<br>Datei ".$_FILES['datei']['name'][$i]." einwandfrei empfangen und gesichert.");
		else
			array_push($uploadlog, "<br>Datei ".$_FILES['datei']['name'][$i]." machte Probleme.");
}
fclose($log);
?>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
	<meta http-equiv="Content-Type" content="text/html;CHARSET=iso-8859-1">
</head>
<body>
<?php
echo count($uploadlog)." Dateien empfangen";
foreach ($uploadlog as $text)
	echo $text;
?>
</body>
</html>
main.cpp

Code: Alles auswählen

#include <QCoreApplication>
#include "httppost.h"

void displayHelp()
{
	printf("Usage: HTTPpost.exe url='URI' file='file.1' file='file.n' [page='result.html']\n");
	printf("  -u\t= gueltige Internetseite\n");
	printf("  -f\t= ein oder mehrere Files vom lokalen Rechner\n");
	printf("Optional\n");
	printf("  -p\t= Name der Datei in der der empfangene Text gespeichert wird.\n");
	printf("    \t  Wird hinterher im Standardviewer angezeigt, default ist result.html\n\n");
	exit(0);
}

int main(int argc, char *argv[])
{
	QCoreApplication a(argc, argv);
	HTTPpost w;
	w.setApp(&a);

	if (argc < 2)
		displayHelp();

	QString param, url;
	QStringList sl, files;
	for (int l=1; l<argc; l++)
	{
		param = argv[l];
		sl = param.split("=", QString::SkipEmptyParts);
		if (sl.count()!=2)
			displayHelp();
		else if (sl[0].toLower()=="-u")
			w.setURI(sl[1]);
		else if (sl[0].toLower()=="-p")
			w.setPage(sl[1]);
		else if (sl[0].toLower()=="-f")
			w.appendFile(sl[1].replace("'","").replace("\"",""));
		else
			displayHelp();
	}

	w.send();
	return a.exec();
}
httppost.h

Code: Alles auswählen

#ifndef HTTPPOST_H
#define HTTPPOST_H

#include <QHttp>
#include <QFile>
#include <QBuffer>

class HTTPpost : public QObject
{
    Q_OBJECT

public:
	HTTPpost();
	~HTTPpost();
	void	send();
	void	appendFile(QString name)	{ files.append(name); };
	void	setURI(QString url)			{ urlName = url; };
	void	setPage(QString page)		{ resultFile = page; };
	void	setApp(QCoreApplication* a)	{ app = a; };

private:
	QCoreApplication* app;
	QByteArray*	bytes;
	QBuffer*	buffer;
	QHttp*		http;
	QFile*		file;
	QFile*		logFile;
	QString		urlName;
	QString		resultFile;
	int			httpPostId;
	QStringList	files;

private slots:
	void httpRequestFinished(int requestId, bool error);
    void readResponseHeader(const QHttpResponseHeader &responseHeader);
};

#endif // HTTPPOST_H
httppost.cpp

Code: Alles auswählen

#include "httppost.h"

#include <QUrl>
#include <QFileInfo>
#include <QDir>
#include <QCoreApplication>

// Wird nur für ShellExecute benötigt
//#include <windows.h>

#define STRFILEVER "1.0.0"

HTTPpost::HTTPpost()
{
	http = new QHttp();
	connect(http, SIGNAL(requestFinished(int, bool)), this, SLOT(httpRequestFinished(int, bool)));
	connect(http, SIGNAL(responseHeaderReceived(const QHttpResponseHeader &)), this, SLOT(readResponseHeader(const QHttpResponseHeader &)));
	resultFile = "";
	app = NULL;
}

HTTPpost::~HTTPpost()
{

}


void HTTPpost::send()
{
	QString version(STRFILEVER);
	version.replace(",",".").replace(" ","");
	QUrl url(urlName);
	http->setHost(url.host(), 80);
	if (!url.userName().isEmpty())
		http->setUser(url.userName(), url.password());

	QHttpRequestHeader header("POST", url.path());
	header.setValue("Host", url.host());
	header.setValue("User-Agent", "HTTPpost.exe " + version);
	header.setValue("Accept", "text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5");
	header.setValue("Accept-Language", "de-de,de;q=0.8,en-us;q=0.5,en;q=0.3");
	header.setValue("Accept-Encoding", "gzip,deflate");
	header.setValue("Accept-Charset", "ISO-8859-1,utf-8;q=0.7,*;q=0.7");
	header.setValue("Keep-Alive", "300");
	header.setValue("Connection", "keep-alive");
	header.setValue("Referer", "HTTPpost.exe " + version);
	// nur nötig falls Passwort geschützt, dann muss aber der Hash-Wert ersetzt werden
//	header.setValue("Authorization", "Basic SG9sem1hOmN1dDJzaXpl");
	header.setValue("Content-Type", "multipart/form-data boundary=---------------------------125282808721657");
	bytes = new QByteArray();
	bytes->append("-----------------------------125282808721657\n");	// Das hier ist ein Dummy-Feldname, damit
	bytes->append("Content-Disposition: form-data; name=\"upload\"\n");	// das auch als POST wahr genommen wird
	bytes->append("\nUploadFile\n");
	// und jetzt kommen die einzelnen Files
	QString name, dateiName;
	file = new QFile();
	for (int l=0; l<files.count(); l++)
	{
		QFileInfo fileInfo(files[l]);
		file->setFileName(files[l]);
		dateiName = fileInfo.fileName();
		if (!file->exists())
		{
			printf("File %S doesn't exist.\n", files[l].utf16());
			delete file;
			file = 0;
			exit(-1);
		}
		if (!file->open(QIODevice::ReadOnly)) 
		{
			printf("Unable to read the file %S: %S.",dateiName.utf16(), file->errorString().utf16());
			delete file;
			file = 0;
			exit(-1);
		}

		bytes->append("-----------------------------125282808721657\n");
		bytes->append("Content-Disposition: form-data; name=\"datei[" + QString::number(l));
		bytes->append("]\"; filename=\"" + dateiName + "\"\n");
		bytes->append("Content-length: " + QString::number(fileInfo.size()));
		bytes->append("\nContent-Type: text/plain\n\n");
		bytes->append(file->readAll());
		file->close();
		bytes->append("\n");
	}
	bytes->append("-----------------------------125282808721657--\n");

	buffer = new QBuffer(bytes);
	if (resultFile=="")
		resultFile = "result.html";
	QDir d;
	if (d.exists(resultFile))
		d.remove(resultFile);
	logFile = new QFile(resultFile);
	logFile->open(QIODevice::ReadWrite);
	httpPostId = http->request(header, buffer, logFile);
}


void HTTPpost::httpRequestFinished(int requestId, bool error)
{
	if (requestId != httpPostId)
		return;

	buffer->close();
	logFile->close();
	QFileInfo logFileInfo(resultFile);
	// der ShellExecute zeigt nur das Logfile an, zu Testzwecken ganz praktisch
//	ShellExecute(NULL, QString("open").toAscii(), logFileInfo.absoluteFilePath().toAscii(), NULL, NULL, SW_SHOW);
	app->quit();
}


void HTTPpost::readResponseHeader(const QHttpResponseHeader &responseHeader)
{
	printf("%d %S\n",responseHeader.statusCode(), responseHeader.reasonPhrase().utf16());
}
chronos
Beiträge: 34
Registriert: 20. September 2005 09:17
Kontaktdaten:

Beitrag von chronos »

Hallo Macman,

wie funktioniert das mit dem Passwort Schutz?
Wie muss ich das Skript auf dem Server schützen?

mfg Benjamin
daniel79
Beiträge: 6
Registriert: 15. Mai 2006 12:01
Wohnort: Saarbrücken

Beitrag von daniel79 »

@macman: Danke für die zur Verfügung-Stellung des Quelltextes. Hat mir schon sehr weitergeholfen.

Jetzt will ich allerdings neben der Datei noch einige Parameter per POST übergeben, die z.B. festlegen in welchem Verzeichnis die hochgeladene Datei landen soll.
Mein erster Gedanke war, einen Parameter nach dem Muster:
bytes->append("\nTestVar=DisplayMe");
hinter der Datei anzuhängen und die Content-length entsprechend zu erhöhen.
Wenn ich jetzt allerdings in der Transfer.php TestVar per echo ausgeben will, scheint diese leer zu sein. Am PHP-Skript kann dies nicht liegen, da es über ein Formular gesandte Werte korrekt ausgibt.
Deshalb vermute ich, dass ich im header etwas ändern muss, habe dazu aber weder hier noch bei Google etwas gefunden, und durch diverse Modifikationen auch keinen Erfolg gehabt.
Ich hoffe hier kann mir jemand weiterhelfen, wie der Quelltext abgeändert werden müßte, um neben der Datei weitere Parameter zu übertragen?
Viele Grüße, Daniel
macman
Beiträge: 1738
Registriert: 15. Juni 2005 13:33
Wohnort: Gütersloh
Kontaktdaten:

Beitrag von macman »

Mein Tipp, Firefox mit LiveHTTPHeaders. Dann bastel Dir eine Seite mit der Du die Daten übertragen kannst und protokollier mit dem AddOn den Traffic. Damit solltest Du sehen wie es aussehen muss.
Die deutsche Schriftsprache ist case-sensitive. Außerdem gibt es eine Interpunktionsnorm. Wenn manch einer seine Programme genauso schlampig schreibt, wie sein Posting hier, dann sollte er es lieber bleiben lassen.
Antworten