Use database to track uploaded chunks and concatenate at the end.
authorJan Gerber <j@users.mediawiki.org>
Wed, 30 Nov 2011 08:55:16 +0000 (08:55 +0000)
committerJan Gerber <j@users.mediawiki.org>
Wed, 30 Nov 2011 08:55:16 +0000 (08:55 +0000)
follow up r93720

12 files changed:
includes/AutoLoader.php
includes/api/ApiUpload.php
includes/filerepo/FSRepo.php
includes/filerepo/FileRepo.php
includes/installer/MysqlUpdater.php
includes/upload/UploadBase.php
includes/upload/UploadFromChunks.php [new file with mode: 0644]
includes/upload/UploadFromFile.php
includes/upload/UploadFromStash.php
languages/messages/MessagesEn.php
maintenance/archives/patch-uploadstash_chunk.sql [new file with mode: 0644]
maintenance/tables.sql

index 7a1f368..e76adb7 100644 (file)
@@ -839,6 +839,7 @@ $wgAutoloadLocalClasses = array(
        # includes/upload
        'UploadBase' => 'includes/upload/UploadBase.php',
        'UploadFromFile' => 'includes/upload/UploadFromFile.php',
+       'UploadFromChunks' => 'includes/upload/UploadFromChunks.php',
        'UploadFromStash' => 'includes/upload/UploadFromStash.php',
        'UploadFromUrl' => 'includes/upload/UploadFromUrl.php',
        'UploadStash' => 'includes/upload/UploadStash.php',
index c7e51e8..a0d56fe 100644 (file)
@@ -89,8 +89,7 @@ class ApiUpload extends ApiBase {
                } else {
                        $this->verifyUpload();
                }
-
-
                // Check if the user has the rights to modify or overwrite the requested title
                // (This check is irrelevant if stashing is already requested, since the errors
                //  can always be fixed by changing the title)
@@ -100,68 +99,105 @@ class ApiUpload extends ApiBase {
                                $this->dieRecoverableError( $permErrors[0], 'filename' );
                        }
                }
+               // Get the result based on the current upload context: 
+               $result = $this->getContextResult();
 
-               // Prepare the API result
-               $result = array();
+               if ( $result['result'] === 'Success' ) {
+                       $result['imageinfo'] = $this->mUpload->getImageInfo( $this->getResult() );
+               }
+
+               $this->getResult()->addValue( null, $this->getModuleName(), $result );
 
+               // Cleanup any temporary mess
+               $this->mUpload->cleanupTempFile();
+       }
+       /**
+        * Get an uplaod result based on upload context
+        */
+       private function getContextResult(){
                $warnings = $this->getApiWarnings();
                if ( $warnings ) {
-                       $result['result'] = 'Warning';
-                       $result['warnings'] = $warnings;
-                       // in case the warnings can be fixed with some further user action, let's stash this upload
-                       // and return a key they can use to restart it
-                       try {
-                               $result['filekey'] = $this->performStash();
-                               $result['sessionkey'] = $result['filekey']; // backwards compatibility
-                       } catch ( MWException $e ) {
-                               $result['warnings']['stashfailed'] = $e->getMessage();
-                       }
+                       // Get warnings formated in result array format
+                       return $this->getWarningsResult( $warnings );
                } elseif ( $this->mParams['chunk'] ) {
-                       $result['result'] = 'Continue';
-                       $chunk = $request->getFileTempName( 'chunk' );
-                       $chunkSize = $request->getUpload( 'chunk' )->getSize();
-                       if ($this->mParams['offset'] == 0) {
-                               $result['filekey'] = $this->performStash();
-                       } else {
-                               $status = $this->mUpload->appendChunk($chunk, $chunkSize,
-                                                                                                         $this->mParams['offset']);
+                       // Add chunk, and get result
+                       return $this->getChunkResult();
+               } elseif ( $this->mParams['stash'] ) {
+                       // Stash the file and get stash result
+                       return $this->getStashResult();
+               }
+               // This is the most common case -- a normal upload with no warnings
+               // performUpload will return a formatted properly for the API with status
+               return $this->performUpload();
+       }
+       /**
+        * Get Stash Result, throws an expetion if the file could not be stashed. 
+        */
+       private function getStashResult(){
+               $result = array ();
+               // 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
+               try {
+                       $result['result'] = 'Success';
+                       $result['filekey'] = $this->performStash();
+                       $result['sessionkey'] = $result['filekey']; // backwards compatibility
+               } catch ( MWException $e ) {
+                       $this->dieUsage( $e->getMessage(), 'stashfailed' );
+               }
+               return $result;
+       }
+       /**
+        * Get Warnings Result
+        * @param $warnings Array of Api upload warnings
+        */
+       private function getWarningsResult( $warnings ){
+               $result = array();
+               $result['result'] = 'Warning';
+               $result['warnings'] = $warnings;
+               // in case the warnings can be fixed with some further user action, let's stash this upload
+               // and return a key they can use to restart it
+               try {
+                       $result['filekey'] = $this->performStash();
+                       $result['sessionkey'] = $result['filekey']; // backwards compatibility
+               } catch ( MWException $e ) {
+                       $result['warnings']['stashfailed'] = $e->getMessage();
+               }
+               return $result;
+       }
+       /**
+        * Get the result of a chunk upload. 
+        */
+       private function getChunkResult(){
+               $result = array();
+               
+               $result['result'] = 'Continue';
+               $request = $this->getMain()->getRequest();
+               $chunkPath = $request->getFileTempname( 'chunk' );
+               $chunkSize = $request->getUpload( 'chunk' )->getSize();
+               if ($this->mParams['offset'] == 0) {
+                       $result['filekey'] = $this->performStash();
+               } else {
+                       $status = $this->mUpload->addChunk($chunkPath, $chunkSize,
+                                                                               $this->mParams['offset']);
+                       if ( !$status->isGood() ) {
+                               $this->dieUsage( $status->getWikiText(), 'stashfailed' );
+                               return ;
+                       }
+                       $result['filekey'] = $this->mParams['filekey'];
+                       // Check we added the last chunk: 
+                       if( $this->mParams['offset'] + $chunkSize == $this->mParams['filesize'] ) {
+                               $status = $this->mUpload->concatenateChunks();
                                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'] = 'Success';
-                                       }
+                                       return ;
                                }
-                       }
-                       $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
-                       try {
                                $result['result'] = 'Success';
-                               $result['filekey'] = $this->performStash();
-                               $result['sessionkey'] = $result['filekey']; // backwards compatibility
-                       } catch ( MWException $e ) {
-                               $this->dieUsage( $e->getMessage(), 'stashfailed' );
                        }
-               } else {
-                       // This is the most common case -- a normal upload with no warnings
-                       // $result will be formatted properly for the API already, with a status
-                       $result = $this->performUpload();
-               }
-
-               if ( $result['result'] === 'Success' ) {
-                       $result['imageinfo'] = $this->mUpload->getImageInfo( $this->getResult() );
                }
-
-               $this->getResult()->addValue( null, $this->getModuleName(), $result );
-
-               // Cleanup any temporary mess
-               $this->mUpload->cleanupTempFile();
+               $result['offset'] = $this->mParams['offset'] + $chunkSize;
+               return $result;
        }
-
+       
        /**
         * Stash the file and return the file key
         * Also re-raises exceptions with slightly more informative message strings (useful for API)
@@ -244,7 +280,24 @@ class ApiUpload extends ApiBase {
                        $this->dieUsageMsg( array( 'missingparam', 'filename' ) );
                }
 
-               if ( $this->mParams['filekey'] ) {
+               if ( $this->mParams['chunk'] ) {
+                       // Chunk upload
+                       $this->mUpload = new UploadFromChunks();
+                       if( isset( $this->mParams['filekey'] ) ){
+                               // handle new chunk
+                               $this->mUpload->continueChunks(
+                                       $this->mParams['filename'],
+                                       $this->mParams['filekey'],
+                                       $request->getUpload( 'chunk' )
+                               );
+                       } else {
+                               // handle first chunk
+                               $this->mUpload->initialize(
+                                       $this->mParams['filename'],
+                                       $request->getUpload( 'chunk' )
+                               );
+                       }
+               } elseif ( isset( $this->mParams['filekey'] ) ) {
                        // Upload stashed in a previous request
                        if ( !UploadFromStash::isValidKey( $this->mParams['filekey'] ) ) {
                                $this->dieUsageMsg( 'invalid-file-key' );
@@ -253,14 +306,6 @@ class ApiUpload extends ApiBase {
                        $this->mUpload = new UploadFromStash( $this->getUser() );
 
                        $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(
index 3e0ef06..583df73 100644 (file)
@@ -313,11 +313,63 @@ class FSRepo extends FileRepo {
                        wfRestoreWarnings();
                }
        }
-
        /**
+        * Concatenate a list of files into a target file location. 
+        * 
+        * @param $fileList array of files
+        * @param $targetFile String target path
+        * @param $flags Integer: bitwise combination of the following flags:
+        *     self::FILES_ONLY     Mark file as existing only if it is a file (not directory)
+        */
+       function concatenate( $fileList, $targetPath, $flags = 0 ){
+               $status = $this->newGood();
+               // Resolve the virtual URL for taget: 
+               if ( self::isVirtualUrl( $targetPath ) ) {
+                       $targetPath = $this->resolveVirtualUrl( $targetPath );
+                       // empty out the target file:  
+                       if ( is_file( $targetPath ) ){
+                               unlink( $targetPath );
+                       }
+               }
+               foreach( $fileList as $sourcePath ){
+                       // Resolve the virtual URL for source: 
+                       if ( self::isVirtualUrl( $sourcePath ) ) {
+                               $sourcePath = $this->resolveVirtualUrl( $sourcePath );
+                       }
+                       if ( !is_file( $sourcePath ) )
+                               $status->fatal( 'filenotfound', $sourcePath );
+       
+                       if ( !$status->isOk() ){
+                               return $status;
+                       }
+       
+                       // Do the append
+                       $chunk = file_get_contents( $sourcePath );
+                       if( $chunk === false ) {
+                               $status->fatal( 'fileconcatenateerrorread', $sourcePath );
+                               return $status;
+                       }
+                       if( $status->isOk() ) {
+                               if ( file_put_contents( $targetPath, $chunk, FILE_APPEND ) ) {
+                                       $status->value = $targetPath;
+                               } else {
+                                       $status->fatal( 'fileconcatenateerror', $sourcePath,  $targetPath);
+                               }
+                       }
+                       if ( $flags & self::DELETE_SOURCE ) {
+                               unlink( $sourcePath );
+                       }
+               }
+               return $status;
+       }
+       /**
+        * @deprecated 1.19
+        * 
         * @return Status
         */
        function append( $srcPath, $toAppendPath, $flags = 0 ) {
+               wfDeprecated(__METHOD__);
+               
                $status = $this->newGood();
 
                // Resolve the virtual URL
index 72c2a8b..dbda0d1 100644 (file)
@@ -420,6 +420,14 @@ abstract class FileRepo {
        abstract function storeTemp( $originalName, $srcPath );
 
 
+       /**
+        * Concatenate and array of file sources. 
+        * @param $fileList Array of file sources
+        * @param $targetPath String target destination for file.
+        * @throws MWException
+        */
+       abstract function concatenate( $fileList, $targetPath, $flags = 0 );
+       
        /**
         * Append the contents of the source path to the given file, OR queue
         * the appending operation in anticipation of a later appendFinish() call.
index 38605c0..01dd28b 100644 (file)
@@ -190,6 +190,7 @@ class MysqlUpdater extends DatabaseUpdater {
                        array( 'addField', 'archive',       'ar_sha1',          'patch-ar_sha1.sql' ),
                        array( 'addIndex', 'page', 'page_redirect_namespace_len', 'patch-page_redirect_namespace_len.sql' ),
                        array( 'modifyField', 'user', 'ug_group', 'patch-ug_group-length-increase.sql' ),
+                       array( 'addField',      'uploadstash',  'us_chunk_inx',         'patch-uploadstash_chunk.sql' ),
                );
        }
 
index 39b4ad2..5a4e269 100644 (file)
@@ -199,23 +199,31 @@ abstract class UploadBase {
        /**
         * Append a file to the Repo file
         *
+        * @deprecated since 1.19
+        * 
         * @param $srcPath String: path to source file
         * @param $toAppendPath String: path to the Repo file that will be appended to.
         * @return Status Status
         */
        protected function appendToUploadFile( $srcPath, $toAppendPath ) {
+               wfDeprecated(__METHOD__);
+               
                $repo = RepoGroup::singleton()->getLocalRepo();
                $status = $repo->append( $srcPath, $toAppendPath );
                return $status;
        }
-
+       
        /**
         * Finish appending to the Repo file
-        *
+        * 
+        * @deprecated since 1.19
+        * 
         * @param $toAppendPath String: path to the Repo file that will be appended to.
         * @return Status Status
         */
        protected function appendFinish( $toAppendPath ) {
+               wfDeprecated(__METHOD__);
+               
                $repo = RepoGroup::singleton()->getLocalRepo();
                $status = $repo->appendFinish( $toAppendPath );
                return $status;
@@ -760,7 +768,6 @@ abstract class UploadBase {
        public function stashFile() {
                // was stashSessionFile
                $stash = RepoGroup::singleton()->getLocalRepo()->getUploadStash();
-
                $file = $stash->stashFile( $this->mTempPath, $this->getSourceType() );
                $this->mLocalFile = $file;
                return $file;
diff --git a/includes/upload/UploadFromChunks.php b/includes/upload/UploadFromChunks.php
new file mode 100644 (file)
index 0000000..b091619
--- /dev/null
@@ -0,0 +1,253 @@
+<?php
+/**
+ * Implements uploading from chunks
+ *
+ * @file
+ * @ingroup upload
+ * @author Michael Dale
+ */
+
+class UploadFromChunks extends UploadFromFile {
+       protected $mOffset, $mChunkIndex, $mFileKey, $mVirtualTempPath;
+       
+       /**
+        * Setup local pointers to stash, repo and user ( similar to UploadFromStash )
+        * 
+        * @param $user User
+        * @param $stash UploadStash
+        * @param $repo FileRepo
+        */
+       public function __construct( $user = false, $stash = false, $repo = false ) {
+               // user object. sometimes this won't exist, as when running from cron.
+               $this->user = $user;
+
+               if( $repo ) {
+                       $this->repo = $repo;
+               } else {
+                       $this->repo = RepoGroup::singleton()->getLocalRepo();
+               }
+
+               if( $stash ) {
+                       $this->stash = $stash;
+               } else {
+                       if( $user ) {
+                               wfDebug( __METHOD__ . " creating new UploadFromChunks instance for " . $user->getId() . "\n" );
+                       } else {
+                               wfDebug( __METHOD__ . " creating new UploadFromChunks instance with no user\n" );
+                       }
+                       $this->stash = new UploadStash( $this->repo, $this->user );
+               }
+
+               return true;
+       }
+       /**
+        * Calls the parent stashFile and updates the uploadsession table to handle "chunks" 
+        *
+        * @return UploadStashFile stashed file
+        */
+       public function stashFile() {
+               // Stash file is the called on creating a new chunk session: 
+               $this->mChunkIndex = 0;
+               $this->mOffset = 0;
+               // Create a local stash target
+               $this->mLocalFile = parent::stashFile();
+               // Update the initial file offset ( based on file size ) 
+               $this->mOffset = $this->mLocalFile->getSize();
+               $this->mFileKey = $this->mLocalFile->getFileKey();
+
+               // Output a copy of this first to chunk 0 location:
+               $status = $this->outputChunk( $this->mLocalFile->getPath() );
+               
+               // Update db table to reflect initial "chunk" state 
+               $this->updateChunkStatus();
+               return $this->mLocalFile;
+       }
+       
+       /**
+        * Continue chunk uploading
+        */     
+       public function continueChunks( $name, $key, $webRequestUpload ) {
+               $this->mFileKey = $key;
+               $this->mUpload = $webRequestUpload;
+               // Get the chunk status form the db: 
+               $this->getChunkStatus();
+               
+               $metadata = $this->stash->getMetadata( $key );
+               $this->initializePathInfo( $name,
+                       $this->getRealPath ( $metadata['us_path'] ),
+                       $metadata['us_size'],
+                       false
+               );
+       }
+       
+       /**
+        * Append the final chunk and ready file for parent::performUpload()
+        * @return void
+        */
+       public function concatenateChunks() {
+               wfDebug( __METHOD__ . " concatenate {$this->mChunkIndex} chunks:" . 
+                                       $this->getOffset() . ' inx:' . $this->getChunkIndex() . "\n" );
+                                       
+               // Concatenate all the chunks to mVirtualTempPath
+               $fileList = Array();
+               // The first chunk is stored at the mVirtualTempPath path so we start on "chunk 1"
+               for( $i = 0; $i <= $this->getChunkIndex(); $i++ ){
+                       $fileList[] = $this->getVirtualChunkLocation( $i );
+               }
+
+               // Concatinate into the mVirtualTempPath location;
+               $status = $this->repo->concatenate( $fileList,  $this->mVirtualTempPath, FileRepo::DELETE_SOURCE );
+               if( !$status->isOk() ){
+                       return $status; 
+               }
+               // Update the mTempPath variable ( for FileUpload or normal Stash to take over )  
+               $this->mTempPath = $this->getRealPath( $this->mVirtualTempPath );
+               return $status;
+       }
+       /**
+        * Returns the virtual chunk location:  
+        * @param unknown_type $index
+        */
+       function getVirtualChunkLocation( $index ){
+               return $this->repo->getVirtualUrl( 'temp' ) . 
+                               '/' .
+                               $this->repo->getHashPath( 
+                                       $this->getChunkFileKey( $index )
+                               ) . 
+                               $this->getChunkFileKey( $index );
+       }
+       /**
+        * Add a chunk to the temporary directory
+        *
+        * @param $chunkPath path to temporary chunk file
+        * @param $chunkSize size of the current chunk
+        * @param $offset offset of current chunk ( mutch match database chunk offset ) 
+        * @return Status
+        */
+       public function addChunk( $chunkPath, $chunkSize, $offset ) {
+               // Get the offset before we add the chunk to the file system
+               $preAppendOffset = $this->getOffset();
+               
+               if ( $preAppendOffset + $chunkSize > $this->getMaxUploadSize()) {
+                       $status = Status::newFatal( 'file-too-large' );
+               } else {
+                       // Make sure the client is uploading the correct chunk with a matching offset.
+                       if ( $preAppendOffset == $offset ) {
+                               // Update local chunk index for the current chunk   
+                               $this->mChunkIndex++;
+                               $status = $this->outputChunk( $chunkPath );
+                               if( $status->isGood() ){
+                                       // Update local offset: 
+                                       $this->mOffset = $preAppendOffset + $chunkSize;
+                                       // Update chunk table status db         
+                                       $this->updateChunkStatus();             
+                               }
+                       } else {
+                               $status = Status::newFatal( 'invalid-chunk-offset' );
+                       }
+               }
+               return $status;
+       }
+       
+       /**
+        * Update the chunk db table with the current status: 
+        */
+       private function updateChunkStatus(){
+               wfDebug( __METHOD__ . " update chunk status for {$this->mFileKey} offset:" . 
+                                       $this->getOffset() . ' inx:' . $this->getChunkIndex() . "\n" );
+               
+               $dbw = $this->repo->getMasterDb();
+               $dbw->update(
+                       'uploadstash',
+                       array( 
+                               'us_status' => 'chunks',
+                               'us_chunk_inx' => $this->getChunkIndex(),
+                               'us_size' => $this->getOffset()
+                       ),
+                       array( 'us_key' => $this->mFileKey ),
+                       __METHOD__
+               );
+       }
+       /**
+        * Get the chunk db state and populate update relevant local values
+        */
+       private function getChunkStatus(){
+               $dbr = $this->repo->getSlaveDb();
+               $row = $dbr->selectRow(
+                       'uploadstash', 
+                       array( 
+                               'us_chunk_inx',
+                               'us_size',
+                               'us_path',
+                       ),
+                       array( 'us_key' => $this->mFileKey ),
+                       __METHOD__
+               );
+               // Handle result:
+               if ( $row ) {
+                       $this->mChunkIndex = $row->us_chunk_inx;
+                       $this->mOffset = $row->us_size;
+                       $this->mVirtualTempPath = $row->us_path;
+               }
+       }
+       /**
+        * Get the current Chunk index 
+        * @return Integer index of the current chunk
+        */
+       private function getChunkIndex(){
+               if( $this->mChunkIndex !== null ){
+                       return $this->mChunkIndex;
+               }
+               return 0;
+       }
+       
+       /**
+        * Gets the current offset in fromt the stashedupload table 
+        * @return Integer current byte offset of the chunk file set 
+        */
+       private function getOffset(){
+               if ( $this->mOffset !== null ){
+                       return $this->mOffset;
+               }
+               return 0;
+       }
+       
+       /**
+        * Output the chunk to disk
+        * 
+        * @param $chunk 
+        * @param unknown_type $path
+        */
+       private function outputChunk( $chunkPath ){
+               // Key is fileKey + chunk index
+               $fileKey = $this->getChunkFileKey();
+               
+               // Store the chunk per its indexed fileKey: 
+               $hashPath = $this->repo->getHashPath( $fileKey );               
+               $storeStatus = $this->repo->store( $chunkPath, 'temp', "$hashPath$fileKey" );
+               
+               // Check for error in stashing the chunk:
+               if ( ! $storeStatus->isOK() ) {
+                       $error = $storeStatus->getErrorsArray();
+                       $error = reset( $error );
+                       if ( ! count( $error ) ) {
+                               $error = $storeStatus->getWarningsArray();
+                               $error = reset( $error );
+                               if ( ! count( $error ) ) {
+                                       $error = array( 'unknown', 'no error recorded' );
+                               }
+                       }
+                       throw new UploadChunkFileException( "error storing file in '$path': " . implode( '; ', $error ) );
+               }
+               return $storeStatus;
+       }
+       private function getChunkFileKey( $index = null ){
+               if( $index === null ){
+                       $index = $this->getChunkIndex();
+               }
+               return $this->mFileKey . '.' . $index ;
+       }
+}
+
+class UploadChunkZeroLengthFileException extends MWException {};
+class UploadChunkFileException extends MWException {};
index c2ab646..6d9e71a 100644 (file)
@@ -75,12 +75,4 @@ class UploadFromFile extends UploadBase {
                
                return parent::verifyUpload();
        }
-
-       /** 
-        * Get the path to the file underlying the upload
-        * @return String path to file
-        */
-       public function getFileTempname() {
-               return $this->mUpload->getTempname();
-       }
 }
index d67cebd..f34f156 100644 (file)
@@ -161,40 +161,4 @@ class UploadFromStash extends UploadBase {
                $this->unsaveUploadedFile();
                return $rv;
        }
-
-       /**
-        * Append a chunk to the temporary file.
-        *
-        * @param $chunk
-        * @param $chunkSize
-        * @param $offset
-        * @return Status
-        */
-       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 );
-       }
 }
index 4afa8b4..c34885a 100644 (file)
@@ -2252,6 +2252,7 @@ It cannot be properly checked for security.',
 'uploadstash-badtoken' => 'Performing of that action was unsuccessful, perhaps because your editing credentials expired. Try again.',
 'uploadstash-errclear' => 'Clearing the files was unsuccessful.',
 'uploadstash-refresh'  => 'Refresh the list of files',
+'invalid-chunk-offset' =>  'Invalid chunk offset',
 
 # img_auth script messages
 'img-auth-accessdenied'     => 'Access denied',
diff --git a/maintenance/archives/patch-uploadstash_chunk.sql b/maintenance/archives/patch-uploadstash_chunk.sql
new file mode 100644 (file)
index 0000000..29e4187
--- /dev/null
@@ -0,0 +1,3 @@
+-- Adding us_chunk_inx field
+ALTER TABLE /*$wgDBprefix*/uploadstash
+  ADD us_chunk_inx int unsigned NULL;
index a4e107a..1900bbb 100644 (file)
@@ -966,6 +966,8 @@ CREATE TABLE /*_*/uploadstash (
        us_timestamp varbinary(14) not null,
 
        us_status varchar(50) not null,
+       
+       us_chunk_inx int unsigned NULL,
 
        -- file properties from File::getPropsFromPath.  these may prove unnecessary.
        --