Browse Source

Report upload progress through a callback function

Thomas Jost 12 years ago
parent
commit
077fe41977
2 changed files with 78 additions and 2 deletions
  1. 71
    1
      FileDropper.py
  2. 7
    1
      ffx-backup.py

+ 71
- 1
FileDropper.py View File

@@ -17,7 +17,7 @@
17 17
 
18 18
 """Interact with FileDropper.com"""
19 19
 
20
-import os.path, re, urllib, urllib2
20
+import os.path, re, socket, urllib, urllib2
21 21
 from types import StringTypes
22 22
 
23 23
 from BeautifulSoup import BeautifulSoup
@@ -67,6 +67,76 @@ class FileDropperException(Exception):
67 67
     def __str__(self):
68 68
         return "[FileDropper error %d] %s" % (self.errno, self.errmsg)
69 69
 
70
+# Support for reporting upload progress through a callback.
71
+# This is quite ugly and hackish, but I didn't want to reimplement both
72
+# httplib and urllib2 just to achieve this :)
73
+# Big huge warning: upload_callback is common to all FileDropper instances,
74
+# so beware if you want to use it in a multithreaded environment!
75
+#
76
+# upload_callback must be set to None or to a callable accepting 2 arguments.
77
+# The first one will be the number of sent bytes, the second one the total
78
+# number of bytes to send or -1 if this isn't known in advance (for example
79
+# when reading data from an iterable...)
80
+# Usually, the size is known for headers but not for a file (read as an
81
+# iterable to avoid loading it in RAM).
82
+upload_callback = None
83
+psshc = poster.streaminghttp.StreamingHTTPConnection
84
+class FileDropperHTTPConnection(psshc):
85
+    def send(self, value):
86
+        """Send ``value`` to the server.
87
+
88
+        ``value`` can be a string object, a file-like object that supports
89
+        a .read() method, or an iterable object that supports a .next()
90
+        method.
91
+        """
92
+        # Based on python 2.6's httplib.HTTPConnection.send()
93
+        if self.sock is None:
94
+            if self.auto_open:
95
+                self.connect()
96
+            else:
97
+                raise NotConnected()
98
+
99
+        # send the data to the server. if we get a broken pipe, then close
100
+        # the socket. we want to reconnect when somebody tries to send again.
101
+        #
102
+        # NOTE: we DO propagate the error, though, because we cannot simply
103
+        #       ignore the error... the caller will know if they can retry.
104
+        if self.debuglevel > 0:
105
+            print "send:", repr(value)
106
+        try:
107
+            blocksize=8192
108
+            total=-1
109
+            try: total=len(value)
110
+            except TypeError: pass
111
+            sent=0
112
+            if hasattr(value,'read') :
113
+                if self.debuglevel > 0: print "sendIng a read()able"
114
+                print "sendIng a read()able"
115
+                data=value.read(blocksize)
116
+                while data:
117
+                    self.sock.sendall(data)
118
+                    sent+=len(data)
119
+                    if callable(upload_callback):
120
+                        upload_callback(sent, total)
121
+                    data=value.read(blocksize)
122
+            elif hasattr(value,'next'):
123
+                if self.debuglevel > 0: print "sendIng an iterable"
124
+                print "sendIng an iterable"
125
+                for data in value:
126
+                    self.sock.sendall(data)
127
+                    sent+=len(data)
128
+                    if callable(upload_callback):
129
+                        upload_callback(sent, total)
130
+            else:
131
+                self.sock.sendall(value)
132
+                if callable(upload_callback):
133
+                    upload_callback(total, total)
134
+        except socket.error, v:
135
+            if v[0] == 32:      # Broken pipe
136
+                self.close()
137
+            raise
138
+poster.streaminghttp.StreamingHTTPConnection = FileDropperHTTPConnection
139
+
70 140
 class FileDropper:
71 141
     """Builds an empty FileDropper object that may be used for uploading files
72 142
     to FileDropper.com, get details about files in a premium account, change

+ 7
- 1
ffx-backup.py View File

@@ -77,7 +77,8 @@ tar.close()
77 77
 
78 78
 # Display archive name and sizes
79 79
 print "Profile saved to %s" % archive_name
80
-print "%.1f MB --> %.1f MB" % (total_size/(1024**2), os.path.getsize(archive_name)/(1024**2))
80
+archive_size = os.path.getsize(archive_name)
81
+print "%.1f MB --> %.1f MB" % (total_size/(1024**2), archive_size/(1024**2))
81 82
 
82 83
 # Encrypt the archive if needed
83 84
 if options.crypt_to is not None:
@@ -111,6 +112,11 @@ if options.crypt_to is not None:
111 112
 if options.fd_user is not None and options.fd_pass is not None:
112 113
     import FileDropper
113 114
 
115
+    def cb(sent, total):
116
+        if total < 0:
117
+            print "Upload: %.4f %%" % (float(sent)/float(archive_size))
118
+    FileDropper.upload_callback = cb
119
+
114 120
     fd = FileDropper.FileDropper()
115 121
     fd.login(options.fd_user, options.fd_pass)
116 122
 

Loading…
Cancel
Save