Package muntjac :: Package ui :: Module upload
[hide private]
[frames] | no frames]

Source Code for Module muntjac.ui.upload

  1  # Copyright (C) 2012 Vaadin Ltd.  
  2  # Copyright (C) 2012 Richard Lincoln 
  3  #  
  4  # Licensed under the Apache License, Version 2.0 (the "License");  
  5  # you may not use this file except in compliance with the License.  
  6  # You may obtain a copy of the License at  
  7  #  
  8  #     http://www.apache.org/licenses/LICENSE-2.0  
  9  #  
 10  # Unless required by applicable law or agreed to in writing, software  
 11  # distributed under the License is distributed on an "AS IS" BASIS,  
 12  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  
 13  # See the License for the specific language governing permissions and  
 14  # limitations under the License. 
 15   
 16  """For uploading files from client to server.""" 
 17   
 18  from warnings import warn 
 19   
 20  from muntjac.ui.abstract_component import AbstractComponent 
 21   
 22  from muntjac.ui.component import \ 
 23      IFocusable, Event as ComponentEvent 
 24   
 25  from muntjac.terminal.stream_variable import \ 
 26      IStreamVariable, IStreamingEvent 
 27   
 28  from muntjac.terminal.gwt.server.exceptions import \ 
 29      NoInputStreamException, NoOutputStreamException 
 30   
 31  from muntjac.util import OrderedSet 
 32   
 33   
34 -class IStartedListener(object):
35 """Receives the events when the upload starts. 36 37 @author: Vaadin Ltd. 38 @author: Richard Lincoln 39 @version: 1.1.2 40 """ 41
42 - def uploadStarted(self, event):
43 """Upload has started. 44 45 @param event: 46 the Upload started event. 47 """ 48 raise NotImplementedError
49 50
51 -class IFinishedListener(object):
52 """Receives the events when the uploads are ready. 53 54 @author: Vaadin Ltd. 55 @author: Richard Lincoln 56 @version: 1.1.2 57 """ 58
59 - def uploadFinished(self, event):
60 """Upload has finished. 61 62 @param event: 63 the Upload finished event. 64 """ 65 raise NotImplementedError
66 67
68 -class IFailedListener(object):
69 """Receives events when the uploads are finished, but unsuccessful. 70 71 @author: Vaadin Ltd. 72 @author: Richard Lincoln 73 @version: 1.1.2 74 """ 75
76 - def uploadFailed(self, event):
77 """Upload has finished unsuccessfully. 78 79 @param event: 80 the Upload failed event. 81 """ 82 raise NotImplementedError
83 84
85 -class ISucceededListener(object):
86 """Receives events when the uploads are successfully finished. 87 88 @author: Vaadin Ltd. 89 @author: Richard Lincoln 90 @version: 1.1.2 91 """ 92
93 - def uploadSucceeded(self, event):
94 """Upload successfull.. 95 96 @param event: 97 the Upload successful event. 98 """ 99 raise NotImplementedError
100 101
102 -class IProgressListener(object):
103 """IProgressListener receives events to track progress of upload.""" 104
105 - def updateProgress(self, readBytes, contentLength):
106 """Updates progress to listener. 107 108 @param readBytes: 109 bytes transferred 110 @param contentLength: 111 total size of file currently being uploaded, -1 if unknown 112 """ 113 raise NotImplementedError
114 115 116 _UPLOAD_FINISHED_METHOD = getattr(IFinishedListener, 'uploadFinished') 117 _UPLOAD_FAILED_METHOD = getattr(IFailedListener, 'uploadFailed') 118 _UPLOAD_STARTED_METHOD = getattr(IStartedListener, 'uploadStarted') 119 _UPLOAD_SUCCEEDED_METHOD = getattr(ISucceededListener, 'uploadSucceeded') 120 121
122 -class Upload(AbstractComponent, IFocusable): #IComponent,
123 """IComponent for uploading files from client to server. 124 125 The visible component consists of a file name input box and a browse 126 button and an upload submit button to start uploading. 127 128 The Upload component needs a StringIO to write the uploaded 129 data. You need to implement the upload.IReceiver interface and return the 130 output stream in the receiveUpload() method. 131 132 You can get an event regarding starting (StartedEvent), progress 133 (ProgressEvent), and finishing (FinishedEvent) of upload by implementing 134 IStartedListener, IProgressListener, and IFinishedListener, respectively. 135 The IFinishedListener is called for both failed and succeeded uploads. If 136 you wish to separate between these two cases, you can use 137 ISucceededListener (SucceededEvenet) and IFailedListener (FailedEvent). 138 139 The upload component does not itself show upload progress, but you can use 140 the ProgressIndicator for providing progress feedback by implementing 141 IProgressListener and updating the indicator in updateProgress(). 142 143 Setting upload component immediate initiates the upload as soon as a file 144 is selected, instead of the common pattern of file selection field and 145 upload button. 146 147 Note! Because of browser dependent implementations of <input type="file"> 148 element, setting size for Upload component is not supported. For some 149 browsers setting size may work to some extent. 150 151 @author: Vaadin Ltd. 152 @author: Richard Lincoln 153 @version: 1.1.2 154 """ 155 156 CLIENT_WIDGET = None #ClientWidget(VUpload, LoadStyle.LAZY) 157
158 - def __init__(self, caption=None, uploadReceiver=None):
159 """Creates a new instance of Upload. 160 161 The receiver must be set before performing an upload. 162 """ 163 super(Upload, self).__init__() 164 165 #: Should the field be focused on next repaint? 166 self._focus = False 167 168 #: The tab order number of this field. 169 self._tabIndex = 0 170 171 #: The output of the upload is redirected to this receiver. 172 self._receiver = None 173 174 self._isUploading = False 175 176 self._contentLength = -1 177 178 self._totalBytes = None 179 180 self._buttonCaption = 'Upload' 181 182 #: ProgressListeners to which information about progress 183 # is sent during upload 184 self._progressListeners = OrderedSet() 185 186 self._progressCallbacks = dict() 187 188 self._interrupted = False 189 190 self._notStarted = None 191 192 self._nextid = 0 193 194 #: Flag to indicate that submitting file has been requested. 195 self._forceSubmit = None 196 197 if caption: 198 self.setCaption(caption) 199 200 if uploadReceiver is not None: 201 self._receiver = uploadReceiver 202 203 self._streamVariable = None
204 205
206 - def changeVariables(self, source, variables):
207 """Invoked when the value of a variable has changed. 208 209 @see: L{AbstractComponent.changeVariables} 210 """ 211 if 'pollForStart' in variables: 212 idd = variables.get('pollForStart') 213 if not self._isUploading and idd == self._nextid: 214 self._notStarted = True 215 self.requestRepaint() 216 else: 217 pass
218 219
220 - def paintContent(self, target):
221 """Paints the content of this component. 222 223 @param target: 224 Target to paint the content on. 225 @raise PaintException: 226 if the paint operation failed. 227 """ 228 if self._notStarted: 229 target.addAttribute('notStarted', True) 230 self._notStarted = False 231 return 232 233 if self._forceSubmit: 234 target.addAttribute('forceSubmit', True) 235 self._forceSubmit = True 236 return 237 238 # The field should be focused 239 if self._focus: 240 target.addAttribute('focus', True) 241 242 # The tab ordering number 243 if self._tabIndex >= 0: 244 target.addAttribute('tabindex', self._tabIndex) 245 246 target.addAttribute('state', self._isUploading) 247 248 if self._buttonCaption is not None: 249 target.addAttribute('buttoncaption', self._buttonCaption) 250 251 target.addAttribute('nextid', self._nextid) 252 253 # Post file to this strean variable 254 target.addVariable(self, 'action', self.getStreamVariable())
255 256
257 - def addListener(self, listener, iface=None):
258 """Adds an event listener. 259 260 @param listener: 261 the listener to be added. 262 """ 263 if (isinstance(listener, IFailedListener) and 264 (iface is None or issubclass(iface, IFailedListener))): 265 self.registerListener(FailedEvent, 266 listener, _UPLOAD_FAILED_METHOD) 267 268 if (isinstance(listener, IFinishedListener) and 269 (iface is None or issubclass(iface, IFinishedListener))): 270 self.registerListener(FinishedEvent, 271 listener, _UPLOAD_FINISHED_METHOD) 272 273 if (isinstance(listener, IProgressListener) and 274 (iface is None or issubclass(iface, IProgressListener))): 275 self._progressListeners.add(listener) 276 277 if (isinstance(listener, IStartedListener) and 278 (iface is None or issubclass(iface, IStartedListener))): 279 self.registerListener(StartedEvent, 280 listener, _UPLOAD_STARTED_METHOD) 281 282 if (isinstance(listener, ISucceededListener) and 283 (iface is None or issubclass(iface, ISucceededListener))): 284 self.registerListener(SucceededEvent, 285 listener, _UPLOAD_SUCCEEDED_METHOD) 286 287 super(Upload, self).addListener(listener, iface)
288 289
290 - def addCallback(self, callback, eventType=None, *args):
291 if eventType is None: 292 eventType = callback._eventType 293 294 if issubclass(eventType, FailedEvent): 295 self.registerCallback(FailedEvent, callback, None, *args) 296 297 elif issubclass(eventType, FinishedEvent): 298 self.registerCallback(FinishedEvent, callback, None, *args) 299 300 elif issubclass(eventType, IProgressListener): # no progress event 301 self._progressCallbacks[callback] = args 302 303 elif issubclass(eventType, StartedEvent): 304 self.registerCallback(StartedEvent, callback, None, *args) 305 306 elif issubclass(eventType, SucceededEvent): 307 self.registerCallback(SucceededEvent, callback, None, *args) 308 309 else: 310 super(Upload, self).addCallback(callback, eventType, *args)
311 312
313 - def removeListener(self, listener, iface=None):
314 """Removes an event listener. 315 316 @param listener: 317 the listener to be removed. 318 """ 319 if (isinstance(listener, IFailedListener) and 320 (iface is None or issubclass(iface, IFailedListener))): 321 self.withdrawListener(FailedEvent, 322 listener, _UPLOAD_FAILED_METHOD) 323 324 if (isinstance(listener, IFinishedListener) and 325 (iface is None or issubclass(iface, IFinishedListener))): 326 self.withdrawListener(FinishedEvent, 327 listener, _UPLOAD_FINISHED_METHOD) 328 329 if (isinstance(listener, IProgressListener) and 330 (iface is None or issubclass(iface, IProgressListener))): 331 if listener in self._progressListeners: 332 self._progressListeners.remove(listener) 333 334 if (isinstance(listener, IStartedListener) and 335 (iface is None or issubclass(iface, IStartedListener))): 336 self.withdrawListener(StartedEvent, 337 listener, _UPLOAD_STARTED_METHOD) 338 339 if (isinstance(listener, ISucceededListener) and 340 (iface is None or issubclass(iface, ISucceededListener))): 341 self.withdrawListener(SucceededEvent, 342 listener, _UPLOAD_SUCCEEDED_METHOD) 343 344 super(Upload, self).removeListener(listener, iface)
345 346
347 - def removeCallback(self, callback, eventType=None):
348 if eventType is None: 349 eventType = callback._eventType 350 351 if issubclass(eventType, FailedEvent): 352 self.withdrawCallback(FailedEvent, callback) 353 354 elif issubclass(eventType, FinishedEvent): 355 self.withdrawCallback(FinishedEvent, callback) 356 357 elif issubclass(eventType, IProgressListener): # no progress event 358 if callback in self._progressCallbacks: 359 del self._progressListeners[callback] 360 361 elif issubclass(eventType, StartedEvent): 362 self.withdrawCallback(StartedEvent, callback) 363 364 elif issubclass(eventType, SucceededEvent): 365 self.withdrawCallback(SucceededEvent, callback) 366 367 else: 368 super(Upload, self).removeCallback(callback, eventType)
369 370
371 - def fireStarted(self, filename, MIMEType):
372 """Emit upload received event. 373 """ 374 evt = StartedEvent(self, filename, MIMEType, self._contentLength) 375 self.fireEvent(evt)
376 377
378 - def fireUploadInterrupted(self, filename, MIMEType, length, e=None):
379 """Emits the upload failed event. 380 """ 381 if e is None: 382 evt = FailedEvent(self, filename, MIMEType, length) 383 else: 384 evt = FailedEvent(self, filename, MIMEType, length, e) 385 386 self.fireEvent(evt)
387 388
389 - def fireNoInputStream(self, filename, MIMEType, length):
390 evt = NoInputStreamEvent(self, filename, MIMEType, length) 391 self.fireEvent(evt)
392 393
394 - def fireNoOutputStream(self, filename, MIMEType, length):
395 evt = NoOutputStreamEvent(self, filename, MIMEType, length) 396 self.fireEvent(evt)
397 398
399 - def fireUploadSuccess(self, filename, MIMEType, length):
400 """Emits the upload success event. 401 """ 402 evt = SucceededEvent(self, filename, MIMEType, length) 403 self.fireEvent(evt)
404 405
406 - def fireUpdateProgress(self, totalBytes, contentLength):
407 """Emits the progress event. 408 409 @param totalBytes: 410 bytes received so far 411 @param contentLength: 412 actual size of the file being uploaded, if known 413 """ 414 # this is implemented differently than other listeners to 415 # maintain backwards compatibility 416 for l in self._progressListeners: 417 l.updateProgress(totalBytes, contentLength) 418 419 for callback, args in self._progressCallbacks.iteritems(): 420 callback(totalBytes, contentLength, *args)
421 422
423 - def getReceiver(self):
424 """Returns the current receiver. 425 426 @return: the IStreamVariable. 427 """ 428 return self._receiver
429 430
431 - def setReceiver(self, receiver):
432 """Sets the receiver. 433 434 @param receiver: 435 the receiver to set. 436 """ 437 self._receiver = receiver
438 439
440 - def focus(self):
441 super(Upload, self).focus()
442 443
444 - def getTabIndex(self):
445 """Gets the Tabulator index of this IFocusable component. 446 447 @see: L{IFocusable.getTabIndex} 448 """ 449 return self._tabIndex
450 451
452 - def setTabIndex(self, tabIndex):
453 """Sets the Tabulator index of this IFocusable component. 454 455 @see: L{IFocusable.setTabIndex} 456 """ 457 self._tabIndex = tabIndex
458 459
460 - def startUpload(self):
461 """Go into upload state. This is to prevent double uploading on same 462 component. 463 464 Warning: this is an internal method used by the framework and should 465 not be used by user of the Upload component. Using it results in the 466 Upload component going in wrong state and not working. It is currently 467 public because it is used by another class. 468 """ 469 if self._isUploading: 470 raise ValueError, 'uploading already started' 471 472 self._isUploading = True 473 self._nextid += 1
474 475
476 - def interruptUpload(self):
477 """Interrupts the upload currently being received. The interruption 478 will be done by the receiving tread so this method will return 479 immediately and the actual interrupt will happen a bit later. 480 """ 481 if self._isUploading: 482 self._interrupted = True
483 484
485 - def endUpload(self):
486 """Go into state where new uploading can begin. 487 488 Warning: this is an internal method used by the framework and should 489 not be used by user of the Upload component. 490 """ 491 self._isUploading = False 492 self._contentLength = -1 493 self._interrupted = False 494 self.requestRepaint()
495 496
497 - def isUploading(self):
498 return self._isUploading
499 500
501 - def getBytesRead(self):
502 """Gets read bytes of the file currently being uploaded. 503 504 @return: bytes 505 """ 506 return self._totalBytes
507 508
509 - def getUploadSize(self):
510 """Returns size of file currently being uploaded. Value sane only 511 during upload. 512 513 @return: size in bytes 514 """ 515 return self._contentLength
516 517
518 - def setProgressListener(self, progressListener):
519 """This method is deprecated, use addListener(IProgressListener) 520 instead. 521 522 @deprecated: Use addListener(IProgressListener) instead. 523 """ 524 warn('use addListener() instead', DeprecationWarning) 525 526 self.addListener(progressListener, IProgressListener)
527 528
529 - def getProgressListener(self):
530 """This method is deprecated. 531 532 @deprecated: Replaced with addListener/removeListener 533 @return: listener 534 """ 535 warn('replaced with addListener/removeListener', DeprecationWarning) 536 537 if len(self._progressListeners) == 0: 538 return None 539 else: 540 return iter(self._progressListeners).next()
541 542
543 - def getButtonCaption(self):
544 """@return: String to be rendered into button that fires uploading""" 545 return self._buttonCaption
546 547
548 - def setButtonCaption(self, buttonCaption):
549 """In addition to the actual file chooser, upload components have 550 button that starts actual upload progress. This method is used to set 551 text in that button. 552 553 In case the button text is set to null, the button is hidden. In this 554 case developer must explicitly initiate the upload process with 555 L{submitUpload}. 556 557 In case the Upload is used in immediate mode using 558 L{setImmediate}, the file choose (html input with type 559 "file") is hidden and only the button with this text is shown. 560 561 B{Note} the string given is set as is to the button. 562 HTML formatting is not stripped. Be sure to properly validate your 563 value according to your needs. 564 565 @param buttonCaption: 566 text for upload components button. 567 """ 568 self._buttonCaption = buttonCaption 569 self.requestRepaint()
570 571
572 - def submitUpload(self):
573 """Forces the upload the send selected file to the server. 574 575 In case developer wants to use this feature, he/she will most probably 576 want to hide the uploads internal submit button by setting its caption 577 to null with L{setButtonCaption} method. 578 579 Note, that the upload runs asynchronous. Developer should use normal 580 upload listeners to trac the process of upload. If the field is empty 581 uploaded the file name will be empty string and file length 0 in the 582 upload finished event. 583 584 Also note, that the developer should not remove or modify the upload 585 in the same user transaction where the upload submit is requested. The 586 upload may safely be hidden or removed once the upload started event 587 is fired. 588 """ 589 self.requestRepaint() 590 self._forceSubmit = True
591 592
593 - def requestRepaint(self):
594 self._forceSubmit = False 595 super(Upload, self).requestRepaint()
596 597
598 - def getStreamVariable(self):
599 # Handle to terminal via Upload monitors and controls the upload 600 # during it is being streamed. 601 if self._streamVariable is None: 602 self._streamVariable = InnerStreamVariable(self) 603 604 return self._streamVariable
605 606
607 - def getListeners(self, eventType):
608 if issubclass(eventType, IStreamingEvent): 609 return list(self._progressListeners) 610 611 return super(Upload, self).getListeners(eventType)
612 613
614 - def getCallbacks(self, eventType):
615 if issubclass(eventType, IStreamingEvent): 616 return dict(self._progressCallbacks) 617 618 return super(Upload, self).getCallbacks(eventType)
619 620
621 -class InnerStreamVariable(IStreamVariable):
622
623 - def __init__(self, upload):
624 self._upload = upload 625 self._lastStartedEvent = None
626 627
628 - def listenProgress(self):
629 return (self._upload.progressListeners is not None 630 and len(self._upload.progressListeners) > 0)
631 632
633 - def onProgress(self, event):
636 637
638 - def isInterrupted(self):
639 return self._upload.interrupted
640 641
642 - def getOutputStream(self):
643 receiveUpload = self._upload.receiver.receiveUpload( 644 self._lastStartedEvent.getFileName(), 645 self._lastStartedEvent.getMimeType()) 646 self._lastStartedEvent = None 647 return receiveUpload
648 649
650 - def streamingStarted(self, event):
651 self.startUpload() 652 self._upload.contentLength = event.getContentLength() 653 self._upload.fireStarted(event.getFileName(), 654 event.getMimeType()) 655 self._lastStartedEvent = event
656 657
658 - def streamingFinished(self, event):
659 self._upload.fireUploadSuccess(event.getFileName(), 660 event.getMimeType(), event.getContentLength()) 661 self._upload.endUpload() 662 self._upload.requestRepaint()
663 664
665 - def streamingFailed(self, event):
666 exception = event.getException() 667 if isinstance(exception, NoInputStreamException): 668 self._upload.fireNoInputStream(event.getFileName(), 669 event.getMimeType(), 0) 670 671 elif isinstance(exception, NoOutputStreamException): 672 self._upload.fireNoOutputStream(event.getFileName(), 673 event.getMimeType(), 0) 674 else: 675 self._upload.fireUploadInterrupted(event.getFileName(), 676 event.getMimeType(), 0, exception) 677 678 self._upload.endUpload()
679 680
681 -class IReceiver(object):
682 """Interface that must be implemented by the upload receivers to provide 683 the Upload component an output stream to write the uploaded data. 684 685 @author: Vaadin Ltd. 686 @author: Richard Lincoln 687 @version: 1.1.2 688 """ 689
690 - def receiveUpload(self, filename, mimeType):
691 """Invoked when a new upload arrives. 692 693 @param filename: 694 the desired filename of the upload, usually as specified 695 by the client. 696 @param mimeType: 697 the MIME type of the uploaded file. 698 @return: Stream to which the uploaded file should be written. 699 """ 700 raise NotImplementedError
701 702
703 -class FinishedEvent(ComponentEvent):
704 """Upload.FinishedEvent is sent when the upload receives a file, 705 regardless of whether the reception was successful or failed. If 706 you wish to distinguish between the two cases, use either SucceededEvent 707 or FailedEvent, which are both subclasses of the FinishedEvent. 708 709 @author: Vaadin Ltd. 710 @author: Richard Lincoln 711 @version: 1.1.2 712 """ 713
714 - def __init__(self, source, filename, MIMEType, length):
715 """@param source: 716 the source of the file. 717 @param filename: 718 the received file name. 719 @param MIMEType: 720 the MIME type of the received file. 721 @param length: 722 the length of the received file. 723 """ 724 super(FinishedEvent, self).__init__(source) 725 726 #: MIME type of the received file. 727 self._type = MIMEType 728 729 #: Received file name. 730 self._filename = filename 731 732 #: Length of the received file. 733 self._length = length
734 735
736 - def getUpload(self):
737 """Uploads where the event occurred. 738 739 @return: the source of the event. 740 """ 741 return self.getSource()
742 743
744 - def getFilename(self):
745 """Gets the file name. 746 747 @return: the filename. 748 """ 749 return self._filename
750 751
752 - def getMIMEType(self):
753 """Gets the MIME Type of the file. 754 755 @return: the MIME type. 756 """ 757 return self._type
758 759
760 - def getLength(self):
761 """Gets the length of the file. 762 763 @return: the length. 764 """ 765 return self._length
766 767
768 -class FailedEvent(FinishedEvent):
769 """Upload.FailedEvent event is sent when the upload is received, 770 but the reception is interrupted for some reason. 771 772 @author: Vaadin Ltd. 773 @author: Richard Lincoln 774 @version: 1.1.2 775 """ 776
777 - def __init__(self, source, filename, MIMEType, length, reason=None):
778 super(FailedEvent, self).__init__(source, filename, MIMEType, length) 779 780 self._reason = reason
781 782
783 - def getReason(self):
784 """Gets the exception that caused the failure. 785 786 @return: the exception that caused the failure, null if n/a 787 """ 788 return self._reason
789 790
791 -class NoOutputStreamEvent(FailedEvent):
792 """FailedEvent that indicates that an output stream could not be obtained. 793 """ 794
795 - def __init__(self, source, filename, MIMEType, length):
796 super(NoOutputStreamEvent, self).__init__(source, filename, MIMEType, 797 length)
798 799
800 -class NoInputStreamEvent(FailedEvent):
801 """FailedEvent that indicates that an input stream could not be obtained. 802 """ 803
804 - def __init__(self, source, filename, MIMEType, length):
805 super(NoInputStreamEvent, self).__init__(source, filename, MIMEType, 806 length)
807 808
809 -class SucceededEvent(FinishedEvent):
810 """Upload.SucceededEvent event is sent when the upload is received 811 successfully. 812 813 @author: Vaadin Ltd. 814 @author: Richard Lincoln 815 @version: 1.1.2 816 """ 817
818 - def __init__(self, source, filename, MIMEType, length):
819 super(SucceededEvent, self).__init__(source, filename, MIMEType, length)
820 821
822 -class StartedEvent(ComponentEvent):
823 """Upload.StartedEvent event is sent when the upload is started to 824 received. 825 826 @author: Vaadin Ltd. 827 @author: Richard Lincoln 828 @version: 1.1.2 829 """ 830
831 - def __init__(self, source, filename, MIMEType, contentLength):
832 super(StartedEvent, self).__init__(source) 833 834 self._filename = filename 835 836 self._type = MIMEType 837 838 #: Length of the received file. 839 self._length = contentLength
840 841
842 - def getUpload(self):
843 """Uploads where the event occurred. 844 845 @return: the source of the event. 846 """ 847 return self.getSource()
848 849
850 - def getFilename(self):
851 """Gets the file name. 852 853 @return: the filename. 854 """ 855 return self._filename
856 857
858 - def getMIMEType(self):
859 """Gets the MIME Type of the file. 860 861 @return: the MIME type. 862 """ 863 return self._type
864 865
866 - def getContentLength(self):
867 """@return: the length of the file that is being uploaded""" 868 return self._length
869