From: Jan Gerber Date: Tue, 2 Aug 2011 10:13:56 +0000 (+0000) Subject: Extend upload api adding an option to upload files in chunks, X-Git-Tag: 1.31.0-rc.0~28497 X-Git-Url: https://git.cyclocoop.org/%28%28?a=commitdiff_plain;h=65c85b5072836aec303165fc7efc2fb495fdfb9e;p=lhc%2Fweb%2Fwiklou.git Extend upload api adding an option to upload files in chunks, using UploadStash to track the unfinished upload. --- diff --git a/includes/api/ApiUpload.php b/includes/api/ApiUpload.php index ff0533015b..a891b2dedf 100644 --- a/includes/api/ApiUpload.php +++ b/includes/api/ApiUpload.php @@ -58,6 +58,7 @@ class ApiUpload extends ApiBase { $request = $this->getMain()->getRequest(); // Add the uploaded file to the params array $this->mParams['file'] = $request->getFileName( 'file' ); + $this->mParams['chunk'] = $request->getFileName( 'chunk' ); // Copy the session key to the file key, for backward compatibility. if( !$this->mParams['filekey'] && $this->mParams['sessionkey'] ) { @@ -85,7 +86,14 @@ class ApiUpload extends ApiBase { } // Check if the uploaded file is sane - $this->verifyUpload(); + if ( $this->mParams['chunk'] ) { + $maxSize = $this->mUpload->getMaxUploadSize( ); + if( $this->mParams['filesize'] > $maxSize ) { + $this->dieUsage( 'The file you submitted was too large', 'file-too-large' ); + } + } else { + $this->verifyUpload(); + } // Check if the user has the rights to modify or overwrite the requested title @@ -113,6 +121,26 @@ class ApiUpload extends ApiBase { } catch ( MWException $e ) { $result['warnings']['stashfailed'] = $e->getMessage(); } + } elseif ( $this->mParams['chunk'] ) { + $result['result'] = 'Continue'; + $chunk = $request->getFileTempName( 'chunk' ); + $chunkSize = $request->getFileSize( 'chunk' ); + if ($this->mParams['offset'] == 0) { + $result['filekey'] = $this->performStash(); + } else { + $status = $this->mUpload->appendChunk($chunk, $chunkSize, + $this->mParams['offset']); + if ( !$status->isGood() ) { + $this->dieUsage( $status->getWikiText(), 'stashfailed' ); + } else { + $result['filekey'] = $this->mParams['filekey']; + if($this->mParams['offset'] + $chunkSize == $this->mParams['filesize']) { + $this->mUpload->finalizeFile(); + $result['result'] = 'Done'; + } + } + } + $result['offset'] = $this->mParams['offset'] + $chunkSize; } elseif ( $this->mParams['stash'] ) { // Some uploads can request they be stashed, so as not to publish them immediately. // In this case, a failure to stash ought to be fatal @@ -188,9 +216,10 @@ class ApiUpload extends ApiBase { protected function selectUploadModule() { $request = $this->getMain()->getRequest(); - // One and only one of the following parameters is needed - $this->requireOnlyOneParameter( $this->mParams, - 'filekey', 'file', 'url', 'statuskey' ); + // chunk or one and only one of the following parameters is needed + if(!$this->mParams['chunk']) + $this->requireOnlyOneParameter( $this->mParams, + 'filekey', 'file', 'url', 'statuskey' ); if ( $this->mParams['statuskey'] ) { $this->checkAsyncDownloadEnabled(); @@ -234,6 +263,13 @@ class ApiUpload extends ApiBase { $this->mUpload->initialize( $this->mParams['filekey'], $this->mParams['filename'] ); + } elseif ( isset( $this->mParams['chunk'] ) ) { + // Start new Chunk upload + $this->mUpload = new UploadFromFile(); + $this->mUpload->initialize( + $this->mParams['filename'], + $request->getUpload( 'chunk' ) + ); } elseif ( isset( $this->mParams['file'] ) ) { $this->mUpload = new UploadFromFile(); $this->mUpload->initialize( @@ -488,6 +524,10 @@ class ApiUpload extends ApiBase { ), 'stash' => false, + 'filesize' => null, + 'offset' => null, + 'chunk' => null, + 'asyncdownload' => false, 'leavemessage' => false, 'statuskey' => null, @@ -511,6 +551,10 @@ class ApiUpload extends ApiBase { 'sessionkey' => 'Same as filekey, maintained for backward compatibility.', 'stash' => 'If set, the server will not add the file to the repository and stash it temporarily.', + 'chunk' => 'Chunk contents', + 'offset' => 'Offset of chunk in bytes', + 'filesize' => 'Filesize of entire upload', + 'asyncdownload' => 'Make fetching a URL asynchronous', 'leavemessage' => 'If asyncdownload is used, leave a message on the user talk page if finished', 'statuskey' => 'Fetch the upload status for this file key', diff --git a/includes/upload/UploadFromStash.php b/includes/upload/UploadFromStash.php index feb14a87f4..a576859357 100644 --- a/includes/upload/UploadFromStash.php +++ b/includes/upload/UploadFromStash.php @@ -130,4 +130,36 @@ class UploadFromStash extends UploadBase { return $rv; } -} \ No newline at end of file + /** + * Append a chunk to the temporary file. + * + * @return void + */ + public function appendChunk($chunk, $chunkSize, $offset) { + //to use $this->getFileSize() here, db needs to be updated + //in appendToUploadFile for that + $fileSize = $this->stash->getFile( $this->mFileKey )->getSize(); + if ( $fileSize + $chunkSize > $this->getMaxUploadSize()) { + $status = Status::newFatal( 'file-too-large' ); + } else { + //append chunk + if ( $fileSize == $offset ) { + $status = $this->appendToUploadFile( $chunk, + $this->mVirtualTempPath ); + } else { + $status = Status::newFatal( 'invalid-chunk-offset' ); + } + } + return $status; + } + + /** + * Append the final chunk and ready file for parent::performUpload() + * @return void + */ + public function finalizeFile() { + $this->appendFinish ( $this->mVirtualTempPath ); + $this->cleanupTempFile(); + $this->mTempPath = $this->getRealPath( $this->mVirtualTempPath ); + } +}