使用QNetworkAccessManager缓慢上传

时间:2014-01-28 22:14:58

标签: python qt pyqt

我在PyQt4中编写了一个程序,使用post请求上传文件到XFS。一切都很好,但速度极低。

使用另一个程序,我获得了70 Mb / s的吞吐量但是我写的东西我最多只能达到8 Mb / s。低速的原因是什么?

我怀疑我需要使用不同的连接。我搜索并发现Qt上的回复,建议这个QFtp,我不知道它是否适用于http。

编辑:为清除问题添加演示,它几乎涵盖了我上传的所有内容(我没有测试示例代码,但它应该可以正常工作)

edit2 :使用此代码我上传8 Mb / s,而在确切情况下另一个程序给出70 Mb / s,速度有很大差异

我在代码中解释了重要的事情

from PyQt4 import QtCore, QtGui, QtNetwork
import requests
import time
class Window(QtGui.QWidget):
    def __init__(self, address):
        QtGui.QWidget.__init__(self)
        self.address = address
        self.table = QtGui.QTableWidget(self)
        header = self.table.horizontalHeader()
        header.setStretchLastSection(True)
        header.hide()
        self.table.setColumnCount(2)
        self.button = QtGui.QPushButton('Add Upload', self)
        self.button.clicked.connect(self.handleAddUpload)
        layout = QtGui.QVBoxLayout(self)
        layout.addWidget(self.table)
        layout.addWidget(self.button)
        self.netaccess = QtNetwork.QNetworkAccessManager(self)
        self._uploaders = {}

    def handleAddUpload(self):
        #in main program this is a loop actually,to handle batch upload
        #for....
        stream = QtCore.QFile('icon.png')
        if stream.open(QtCore.QIODevice.ReadOnly):
            self.ts = time.time() #we will use this later
            data = stream
            row = self.table.rowCount()
            button = QtGui.QPushButton('Abort', self.table)
            button.clicked.connect(lambda: self.handleAbort(row))
            progress = QtGui.QProgressBar(self.table)
            progress.setRange(1, stream.size())
            self.table.setRowCount(row + 1)
            self.table.setCellWidget(row, 0, button)
            self.table.setCellWidget(row, 1, progress)
            uploader = self._uploaders[row] = Uploader(row, self.netaccess)
            uploader.uploadProgress.connect(self.handleUploadProgress)
            uploader.uploadFinished.connect(self.handleUploadFinished)
            uploader.upload(data, self.address)

    def handleUploadProgress(self, key, sent, total):
        print'upload(%d): %d [%d]' % (key, sent, total)
        #I test it,when I call this three method,upload speed in my local become 3 mb
        #but when I Comment these methods speed is over 70 mb
        #problem is here I guess
        self.humansize(sent, total)
        self.humantransferrate(sent)
        self.timeleft(sent, total)
        #I commented next lines because here we don't have a table but in main
        #program these are not commented and change table cells rapidly

        #self.sentsizelabel = QtGui.QLabel(str(self.humansent))
        #self.totalsizelabel = QtGui.QLabel(str(self.humantotal))
        #self.transferlabel = QtGui.QLabel(str(self.tr))
        #self.timeleftlabel = QtGui.QLabel(str("{0}").format(self.timeLeft))
        #self.table.setCellWidget(key, 1, self.sentsizelabel)
        #self.table.setCellWidget(key, 2, self.totalsizelabel)
        #self.table.setCellWidget(key, 5, self.transferlabel)
        #self.table.setCellWidget(key, 3, self.timeleftlabel)

        progress = self.table.cellWidget(key, 1)
        progress.setValue(sent)

    def handleUploadFinished(self, key):
        print'upload(%d) finished' % key
        button = self.table.cellWidget(key, 0)
        button.setDisabled(True)
        uploader = self._uploaders.pop(key)
        uploader.deleteLater()

    def handleAbort(self, key):
        try:
            self._uploaders[key].abort()
        except (KeyError, AttributeError):
            pass

    def humantransferrate(self,sent): #sorry if it's not clean 
        now = time.time() #get time()
        uploadTime = now - self.ts #self.ts is the time() when uploaded started,uploadTime is time between start upload and now
        self.trfirst = (sent/1024)/uploadTime# turning byte to Kb
        if self.trfirst > 1000:#retun better format of transfer rate
            self.trlast = self.trfirst/1024
            self.tr = str('{0:.1f} Mb'.format(self.trlast))
        else:
            self.tr = str('{0:.2f} Kb'.format(self.trfirst))
        return

    def timeleft(self, sent, total):
        leftSize = total-sent
        now = time.time() #right now
        uploadTime = now - self.ts #self.ts is the time() when upload started
        #compute time left
        #here we use Proportion to figure out time left
        param = leftSize * uploadTime
        try:
            timeLen = param/sent
        except: #this is because for very very small files param/sent will return 0
            timeLen = 0.001
        if timeLen > 60:#return best format
            self.timeLeft = '{0:.1f} min'.format(timeLen / 60)
        else:
            self.timeLeft = '{0:.0f} sec'.format(timeLen)


    def humansize(self, sent, total):
        if sent/1000 > 1000: #return right format
            self.humansent = '{0:.1f} Mb'.format(sent/1000000)
        else:
            self.humansent = '{0:.2f} Kb'.format(sent/1000)
        if total/1000 > 1000: #return right format
            self.humantotal = '{0:.1f} Mb'.format(total/1000000)
        else:
            self.humantotal = '{0:.2f} Kb'.format(total/1000)
        return self

class Uploader(QtCore.QObject):
    uploadProgress = QtCore.pyqtSignal(object, int, int)
    uploadFinished = QtCore.pyqtSignal(object)

    def __init__(self, key, parent):
        QtCore.QObject.__init__(self, parent)
        self._key = key
        self._reply = None

    def abort(self):
        if self._reply is not None:
            self._reply.abort()

    def upload(self, data, url):
        if self._reply is None:
            #in here I read from a json file to get cookies,this is fast I guess
            #with open.....
            self._stream = data
            self._multiPart = QtNetwork.QHttpMultiPart(QtNetwork.QHttpMultiPart.FormDataType)
            fileName = QtCore.QFileInfo(self._stream.fileName()).fileName()
            key = 'file'
            imagePart = QtNetwork.QHttpPart()
            imagePart.setHeader(QtNetwork.QNetworkRequest.ContentDispositionHeader,
                     "form-data; name=\"%s\"; filename=\"%s\"" % (key, fileName))
            imagePart.setBodyDevice(self._stream)
            self._multiPart.append(imagePart)
            #sending login data
            #self._multiPart.append(sessPart)
            #self._multiPart.append(submitPart)
            request = QtNetwork.QNetworkRequest(QtCore.QUrl(url))
            request.setHeader(QtNetwork.QNetworkRequest.ContentTypeHeader,
  'multipart/form-data; boundary=%s' % self._multiPart.boundary())
            request.setRawHeader('User-Agent','Mozilla/5.0')
            request.setRawHeader('Cookie', 'some cookie from json file')
            self._reply = self.parent().post(request, self._multiPart)
            self._reply.uploadProgress.connect(self.handleUploadProgress)
            self._reply.finished.connect(self.handleFinished)

    def handleUploadProgress(self, sent, total):
        #when I comment this and print transfer rate in here speed is over 70 mb
        self.uploadProgress.emit(self._key, sent, total)


    def handleFinished(self):
        #here I use requests again to get uploaded url from an html page(parsing it) I didn't write it because I it's better to code be shorter
        self._stream.close()
        self._multiPart.deleteLater()
        self._reply.deleteLater()
        self._reply = None
        self.uploadFinished.emit(self._key)

if __name__ == '__main__':

    import sys
    app = QtGui.QApplication(sys.argv)
    window = Window('http://localhost/upload.php')
    window.setGeometry(500, 300, 500, 300)
    window.show()
    sys.exit(app.exec_())

我不确定,但我想如果我在Uploader.handleUploadProgress()中发出uploadProgress,每5秒钟就会解决一个问题: - ?但如果有效的话,不知道如何测试

编辑:现在问题解决了,我在Window.UploadProgress中使用并尝试使用if来使其正常工作,但这根本不是好办法,如果你们有任何建议我我很想知道

1 个答案:

答案 0 :(得分:0)

也许您正在使用Python的文件api读取文件,然后通过QByteArray传递字节?

确保您将QFile的实例作为post方法的第二个参数传递。

但首先,发布一个重现问题的最小例子。它可能只有十几行 - 记住,它毕竟是Python。它应该大致如下:

file = QFile("file.zip")
if not file.open(QIODevice.ReadOnly):
    return
request = QNetworkRequest("http://foo.bar.baz/upload")
reply = networkAccessManager.post(request, file)

如果您正在使用多部分请求,它应该与下面的代码大致相同。感谢setParent提示Andrew Suffield

filename = "file.zip"
file = QFile(filename)
if not file.open(QIODevice.ReadOnly):
    return
multipart = QHttpMultiPart(QtNetwork.QHttpMultiPart.FormDataType)
filepart = QHttpPart()
filepart.setHeader(QNetworkRequest.ContentTypeHeader, 'application/octet-stream')
filepart.setHeader(QNetworkRequest.ContentDispositionHeader, 'form-data; name="%s"; filename="%s"' % ('file', filename))
filepart.setBodyDevice(file)
multipart.append(filepart)
reply = manager.post(request, multipart)
# Hook multipart to the reply so that it sticks around for the lifetime of the request
multipart.setParent(reply)

这些应该不会比同等的C ++代码更差,所以如果有问题,只要你的其他代码是理智的,它可能在Qt中正确。