4

I'm trying to send a file and other POST variables to a xfilesharing script(which is in perl) on my customer server.

There are no good resources on Google and the code samples I've found don't work.(actually they were in c++ and I couldn't get them work)

server is using Apache for webserver

I ask a question before and I got a pretty good answer,so I'm using that uploader in here,code just not work for uploading file through http post

so can anyone first of all tell me what I need to do to upload file through HTTP post and then it would be great if you could give me a sample (simple code to upload on a localhost will be enough,I just want to see how to do that and how uploading works)

Community
  • 1
  • 1
Mohammadhzp
  • 498
  • 7
  • 20
  • Is there a reason you aren't using something like urllib2 in the Python standard library? Just curious as to what the benefits of using a qt library for sending POST requests. – three_pineapples Jan 16 '14 at 23:29
  • yes,Actually it's a GUI software which uploading files to a xfilesharing script,for GUI I needed Qt, plus I didn't know how to integrate urllib2 with Qt :( , still have lots of problem in posting files,you have better idea ? – Mohammadhzp Jan 17 '14 at 00:44
  • Well I don't see why you can't just get the information to send with Qt widgets and the use urllib2 to actually do the POST request. For instance you could get the file path to upload with a `QFileDialog` and then follow other stackoverflow posts on using urllib2 (Eg http://stackoverflow.com/questions/4496691/uploading-file-using-urllib2 ). Just because you use Qt for your GUI, doesn't mean you can't call other **non**-GUI libraries from within your code. – three_pineapples Jan 17 '14 at 00:52
  • it's great idea,I did not know that(sorry,I'm new). if just urllib2 do one thing for me: the main reason I used QtNetwork is I needed parallel uploading, can I use(somehow) urllib2 to have parallel uploading in PyQt too ?(asking for just to be sure) if it does I'll switch to urllib2 – Mohammadhzp Jan 17 '14 at 01:01
  • 1
    Ah, yes urllib2 is **not** thread-safe so parallel uploading is out. Fortunately, a quick google search returns the project urllib3 (not included standard with Python, but you can install it pretty simply by running `pip install urllib3` or `easy_install urllib3` from the command line). urllib3 says it is thread-safe and provides better support for uploading files. Check it out: https://github.com/shazow/urllib3 – three_pineapples Jan 17 '14 at 02:07
  • I'm not neccessarily saying you should switch to urllib3, it might be easier to stick with what you have if it works, but just letting you know there are other options available when working with python! – three_pineapples Jan 17 '14 at 02:09
  • Thanks for your helpful comments,It's much better to know other options are available – Mohammadhzp Jan 17 '14 at 07:44

1 Answers1

7

This question appears surprisingly difficult. There is indeed no complete examples on this topic.

PyQt

In PyQt4 I managed to run example provided in the QHttpMultiPart documentation. Adaptated Python version (requires Qt 4.8):

from PyQt4 import QtGui, QtCore, QtNetwork
import sys
import time

def finished(reply):
  print "Finished: ", reply.readAll()
  app.quit()

def construct_multipart(data, files):
  multiPart = QtNetwork.QHttpMultiPart(QtNetwork.QHttpMultiPart.FormDataType)
  for key, value in data.items():
    textPart = QtNetwork.QHttpPart()
    textPart.setHeader(QtNetwork.QNetworkRequest.ContentDispositionHeader,
      "form-data; name=\"%s\"" % key)
    textPart.setBody(value)
    multiPart.append(textPart)

  for key, file in files.items():
    imagePart = QtNetwork.QHttpPart()
    #imagePart.setHeader(QNetworkRequest::ContentTypeHeader, ...);
    fileName = QtCore.QFileInfo(file.fileName()).fileName()
    imagePart.setHeader(QtNetwork.QNetworkRequest.ContentDispositionHeader,
      "form-data; name=\"%s\"; filename=\"%s\"" % (key, fileName))
    imagePart.setBodyDevice(file);
    multiPart.append(imagePart)
  return multiPart

app = QtGui.QApplication(sys.argv)
file1 = QtCore.QFile('/tmp/1.txt')
file1.open(QtCore.QFile.ReadOnly)
url = QtCore.QUrl('http://localhost:3000/qwertytest1');
data = { 'text1': 'test1', 'text2': 'test2' }
files = {'file1': file1 }
multipart = construct_multipart(data, files)
request_qt = QtNetwork.QNetworkRequest(url)
request_qt.setHeader(QtNetwork.QNetworkRequest.ContentTypeHeader,
  'multipart/form-data; boundary=%s' % multipart.boundary())
manager = QtNetwork.QNetworkAccessManager()
manager.finished.connect(finished)
request = manager.post(request_qt, multipart)

sys.exit(app.exec_())

PySide

PySide implementation has QHttpMultiPart missing. The only way is to construct post data contents manually. Luckily, Python has its own libraries to create multipart HTTP requests. Here is what I've written:

import sys
from PySide import QtCore, QtGui, QtNetwork
import requests

def finished(reply):
  print "Finished: ", reply.readAll()
  app.quit()

app = QtGui.QApplication(sys.argv)
url = 'http://localhost:3000/qwertytest1'
data = { 'text1': 'test1', 'text2': 'test2' }
files = {'file1': open('/tmp/1.txt') }
request = requests.Request('POST', url, data=data, files=files).prepare()
request_qt = QtNetwork.QNetworkRequest(url)
for header, value in request.headers.items():
  request_qt.setRawHeader(header, value)
manager = QtNetwork.QNetworkAccessManager()
manager.finished.connect(finished)
request = manager.post(request_qt, request.body)

sys.exit(app.exec_())

Note that this method loads all file content in the memory. It's unacceptable if you're dealing with large files. python-requests module itself supports sending large files dynamically, but there is no way to use this functionality with Qt. You can just use python-requests without Qt if that is the case.

Pavel Strakhov
  • 39,123
  • 5
  • 88
  • 127
  • Hello,Thanks for answer,I'm installing PyQt,it seems I need to change my code to PyQt since files are large,I just have one more problem,I know how to set header for content length but I don't know how to set header for submit a form,I searched for this part too before but no luck.i.e a simple form like this : . what is the header for this form? just a hint is enough. Thanks – Mohammadhzp Jan 16 '14 at 18:27
  • There is no such thing as "header to submit a form". Making POST request means submitting a form. You can use your browser's development tools to get actual headers and form data sent by browser. – Pavel Strakhov Jan 16 '14 at 22:01
  • sorry for asking again,but for no reason I'm getting this error : "QHttpPart.setBodyDevice(QIODevice): argument 1 has unexpected type 'QByteArray'". I didn't use QByteArray at all so what does this mean ? – Mohammadhzp Jan 17 '14 at 17:48
  • @Mohammadhzp. How do you get this error? I tried the PyQt example above, and it seems to work for me without error. – ekhumoro Jan 17 '14 at 19:17
  • I tried to use it in my own software,I paste codes [here](http://paste.ofcode.org/AsvAAnqsAjgf3nVcbBmXEv).(your example) which give error,I can't understand why Pavel code is not working this way – Mohammadhzp Jan 17 '14 at 19:43
  • @Mohammadhzp. The above example uses a `QFile`, which is a `QIODevice`, whereas my example just passes the data as a `QByteArray`. So use [setBody](https://qt-project.org/doc/qt-4.8/qhttppart.html#setBody) instead. Also, you should do `self._multipart = QHttpMultiPart...` and make sure you delete it in properly in `handleFinished`. – ekhumoro Jan 17 '14 at 20:33
  • setBody is not suitable for large files,I even tried QFile but that didn't work either,I follow [docs](https://qt-project.org/doc/qt-4.8/qnetworkreply.html) but options in there didn't help – Mohammadhzp Jan 17 '14 at 20:39
  • @Mohammadhzp. You asked how to fix the code in the link, which I did. But in any case, it is not too hard to adapt it to work with a `QFile`. Just pass the open `stream` to `upload` (instead of reading the data and closing), and use `stream.size()` instead of `len(data)`. You will need to keep a reference to the stream while it is uploading, so do `self._stream = stream` in `upload` and delete it in `handleFinished`. – ekhumoro Jan 17 '14 at 20:53
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/45503/discussion-between-mohammadhzp-and-ekhumoro) – Mohammadhzp Jan 17 '14 at 20:57
  • Ok,finally,thanks to Pavel and ekhumoro,mystery solved,the issue was PyQt4 has a bug on python3 and the exact code which working on python2 is not working on python3(which I was trying to do for days) – Mohammadhzp Jan 24 '14 at 00:56