MediaWiki  1.23.2
UploadFromChunks.php
Go to the documentation of this file.
1 <?php
31  protected $mOffset;
32  protected $mChunkIndex;
33  protected $mFileKey;
34  protected $mVirtualTempPath;
35 
43  public function __construct( $user = null, $stash = false, $repo = false ) {
44  // user object. sometimes this won't exist, as when running from cron.
45  $this->user = $user;
46 
47  if ( $repo ) {
48  $this->repo = $repo;
49  } else {
50  $this->repo = RepoGroup::singleton()->getLocalRepo();
51  }
52 
53  if ( $stash ) {
54  $this->stash = $stash;
55  } else {
56  if ( $user ) {
57  wfDebug( __METHOD__ . " creating new UploadFromChunks instance for " . $user->getId() . "\n" );
58  } else {
59  wfDebug( __METHOD__ . " creating new UploadFromChunks instance with no user\n" );
60  }
61  $this->stash = new UploadStash( $this->repo, $this->user );
62  }
63 
64  return true;
65  }
66 
72  public function stashFile( User $user = null ) {
73  // Stash file is the called on creating a new chunk session:
74  $this->mChunkIndex = 0;
75  $this->mOffset = 0;
76 
77  $this->verifyChunk();
78  // Create a local stash target
79  $this->mLocalFile = parent::stashFile();
80  // Update the initial file offset (based on file size)
81  $this->mOffset = $this->mLocalFile->getSize();
82  $this->mFileKey = $this->mLocalFile->getFileKey();
83 
84  // Output a copy of this first to chunk 0 location:
85  $this->outputChunk( $this->mLocalFile->getPath() );
86 
87  // Update db table to reflect initial "chunk" state
88  $this->updateChunkStatus();
89  return $this->mLocalFile;
90  }
91 
95  public function continueChunks( $name, $key, $webRequestUpload ) {
96  $this->mFileKey = $key;
97  $this->mUpload = $webRequestUpload;
98  // Get the chunk status form the db:
99  $this->getChunkStatus();
100 
101  $metadata = $this->stash->getMetadata( $key );
102  $this->initializePathInfo( $name,
103  $this->getRealPath( $metadata['us_path'] ),
104  $metadata['us_size'],
105  false
106  );
107  }
108 
113  public function concatenateChunks() {
114  $chunkIndex = $this->getChunkIndex();
115  wfDebug( __METHOD__ . " concatenate {$this->mChunkIndex} chunks:" .
116  $this->getOffset() . ' inx:' . $chunkIndex . "\n" );
117 
118  // Concatenate all the chunks to mVirtualTempPath
119  $fileList = array();
120  // The first chunk is stored at the mVirtualTempPath path so we start on "chunk 1"
121  for ( $i = 0; $i <= $chunkIndex; $i++ ) {
122  $fileList[] = $this->getVirtualChunkLocation( $i );
123  }
124 
125  // Get the file extension from the last chunk
126  $ext = FileBackend::extensionFromPath( $this->mVirtualTempPath );
127  // Get a 0-byte temp file to perform the concatenation at
128  $tmpFile = TempFSFile::factory( 'chunkedupload_', $ext );
129  $tmpPath = false; // fail in concatenate()
130  if ( $tmpFile ) {
131  // keep alive with $this
132  $tmpPath = $tmpFile->bind( $this )->getPath();
133  }
134 
135  // Concatenate the chunks at the temp file
136  $tStart = microtime( true );
137  $status = $this->repo->concatenate( $fileList, $tmpPath, FileRepo::DELETE_SOURCE );
138  $tAmount = microtime( true ) - $tStart;
139  if ( !$status->isOk() ) {
140  return $status;
141  }
142  wfDebugLog( 'fileconcatenate', "Combined $i chunks in $tAmount seconds." );
143 
144  // File system path
145  $this->mTempPath = $tmpPath;
146  // Since this was set for the last chunk previously
147  $this->mFileSize = filesize( $this->mTempPath );
148  $ret = $this->verifyUpload();
149  if ( $ret['status'] !== UploadBase::OK ) {
150  wfDebugLog( 'fileconcatenate', "Verification failed for chunked upload" );
151  $status->fatal( $this->getVerificationErrorCode( $ret['status'] ) );
152  return $status;
153  }
154 
155  // Update the mTempPath and mLocalFile
156  // (for FileUpload or normal Stash to take over)
157  $tStart = microtime( true );
158  $this->mLocalFile = parent::stashFile( $this->user );
159  $tAmount = microtime( true ) - $tStart;
160  $this->mLocalFile->setLocalReference( $tmpFile ); // reuse (e.g. for getImageInfo())
161  wfDebugLog( 'fileconcatenate', "Stashed combined file ($i chunks) in $tAmount seconds." );
162 
163  return $status;
164  }
165 
174  public function performUpload( $comment, $pageText, $watch, $user ) {
175  $rv = parent::performUpload( $comment, $pageText, $watch, $user );
176  return $rv;
177  }
178 
184  function getVirtualChunkLocation( $index ) {
185  return $this->repo->getVirtualUrl( 'temp' ) .
186  '/' .
187  $this->repo->getHashPath(
188  $this->getChunkFileKey( $index )
189  ) .
190  $this->getChunkFileKey( $index );
191  }
192 
201  public function addChunk( $chunkPath, $chunkSize, $offset ) {
202  // Get the offset before we add the chunk to the file system
203  $preAppendOffset = $this->getOffset();
204 
205  if ( $preAppendOffset + $chunkSize > $this->getMaxUploadSize() ) {
206  $status = Status::newFatal( 'file-too-large' );
207  } else {
208  // Make sure the client is uploading the correct chunk with a matching offset.
209  if ( $preAppendOffset == $offset ) {
210  // Update local chunk index for the current chunk
211  $this->mChunkIndex++;
212  try {
213  # For some reason mTempPath is set to first part
214  $oldTemp = $this->mTempPath;
215  $this->mTempPath = $chunkPath;
216  $this->verifyChunk();
217  $this->mTempPath = $oldTemp;
219  return Status::newFatal( $e->getMessage() );
220  }
221  $status = $this->outputChunk( $chunkPath );
222  if ( $status->isGood() ) {
223  // Update local offset:
224  $this->mOffset = $preAppendOffset + $chunkSize;
225  // Update chunk table status db
226  $this->updateChunkStatus();
227  }
228  } else {
229  $status = Status::newFatal( 'invalid-chunk-offset' );
230  }
231  }
232  return $status;
233  }
234 
238  private function updateChunkStatus() {
239  wfDebug( __METHOD__ . " update chunk status for {$this->mFileKey} offset:" .
240  $this->getOffset() . ' inx:' . $this->getChunkIndex() . "\n" );
241 
242  $dbw = $this->repo->getMasterDb();
243  // Use a quick transaction since we will upload the full temp file into shared
244  // storage, which takes time for large files. We don't want to hold locks then.
245  $dbw->begin( __METHOD__ );
246  $dbw->update(
247  'uploadstash',
248  array(
249  'us_status' => 'chunks',
250  'us_chunk_inx' => $this->getChunkIndex(),
251  'us_size' => $this->getOffset()
252  ),
253  array( 'us_key' => $this->mFileKey ),
254  __METHOD__
255  );
256  $dbw->commit( __METHOD__ );
257  }
258 
262  private function getChunkStatus() {
263  // get Master db to avoid race conditions.
264  // Otherwise, if chunk upload time < replag there will be spurious errors
265  $dbw = $this->repo->getMasterDb();
266  $row = $dbw->selectRow(
267  'uploadstash',
268  array(
269  'us_chunk_inx',
270  'us_size',
271  'us_path',
272  ),
273  array( 'us_key' => $this->mFileKey ),
274  __METHOD__
275  );
276  // Handle result:
277  if ( $row ) {
278  $this->mChunkIndex = $row->us_chunk_inx;
279  $this->mOffset = $row->us_size;
280  $this->mVirtualTempPath = $row->us_path;
281  }
282  }
283 
288  private function getChunkIndex() {
289  if ( $this->mChunkIndex !== null ) {
290  return $this->mChunkIndex;
291  }
292  return 0;
293  }
294 
299  private function getOffset() {
300  if ( $this->mOffset !== null ) {
301  return $this->mOffset;
302  }
303  return 0;
304  }
305 
313  private function outputChunk( $chunkPath ) {
314  // Key is fileKey + chunk index
315  $fileKey = $this->getChunkFileKey();
316 
317  // Store the chunk per its indexed fileKey:
318  $hashPath = $this->repo->getHashPath( $fileKey );
319  $storeStatus = $this->repo->quickImport( $chunkPath,
320  $this->repo->getZonePath( 'temp' ) . "/{$hashPath}{$fileKey}" );
321 
322  // Check for error in stashing the chunk:
323  if ( ! $storeStatus->isOK() ) {
324  $error = $storeStatus->getErrorsArray();
325  $error = reset( $error );
326  if ( ! count( $error ) ) {
327  $error = $storeStatus->getWarningsArray();
328  $error = reset( $error );
329  if ( ! count( $error ) ) {
330  $error = array( 'unknown', 'no error recorded' );
331  }
332  }
333  throw new UploadChunkFileException( "Error storing file in '$chunkPath': " .
334  implode( '; ', $error ) );
335  }
336  return $storeStatus;
337  }
338 
339  private function getChunkFileKey( $index = null ) {
340  if ( $index === null ) {
341  $index = $this->getChunkIndex();
342  }
343  return $this->mFileKey . '.' . $index;
344  }
345 
351  private function verifyChunk() {
352  // Rest mDesiredDestName here so we verify the name as if it were mFileKey
353  $oldDesiredDestName = $this->mDesiredDestName;
354  $this->mDesiredDestName = $this->mFileKey;
355  $this->mTitle = false;
356  $res = $this->verifyPartialFile();
357  $this->mDesiredDestName = $oldDesiredDestName;
358  $this->mTitle = false;
359  if ( is_array( $res ) ) {
360  throw new UploadChunkVerificationException( $res[0] );
361  }
362  }
363 }
364 
366 }
367 
369 }
370 
372 }
UploadBase\getRealPath
getRealPath( $srcPath)
Definition: UploadBase.php:253
UploadFromChunks\updateChunkStatus
updateChunkStatus()
Update the chunk db table with the current status:
Definition: UploadFromChunks.php:238
RepoGroup\singleton
static singleton()
Get a RepoGroup instance.
Definition: RepoGroup.php:53
php
skin txt MediaWiki includes four core it has been set as the default in MediaWiki since the replacing Monobook it had been been the default skin since before being replaced by Vector largely rewritten in while keeping its appearance Several legacy skins were removed in the as the burden of supporting them became too heavy to bear Those in etc for skin dependent CSS etc for skin dependent JavaScript These can also be customised on a per user by etc This feature has led to a wide variety of user styles becoming that gallery is a good place to ending in php
Definition: skin.txt:62
UploadFromChunks\getOffset
getOffset()
Gets the current offset in fromt the stashedupload table.
Definition: UploadFromChunks.php:299
UploadFromChunks\getChunkIndex
getChunkIndex()
Get the current Chunk index.
Definition: UploadFromChunks.php:288
UploadFromChunks\outputChunk
outputChunk( $chunkPath)
Output the chunk to disk.
Definition: UploadFromChunks.php:313
UploadFromChunks\$mVirtualTempPath
$mVirtualTempPath
Definition: UploadFromChunks.php:34
UploadChunkZeroLengthFileException
Definition: UploadFromChunks.php:365
wfDebugLog
wfDebugLog( $logGroup, $text, $dest='all')
Send a line to a supplementary debug log file, if configured, or main debug log if not.
Definition: GlobalFunctions.php:1040
UploadFromChunks\__construct
__construct( $user=null, $stash=false, $repo=false)
Setup local pointers to stash, repo and user (similar to UploadFromStash)
Definition: UploadFromChunks.php:43
UploadStash
UploadStash is intended to accomplish a few things:
Definition: UploadStash.php:44
$ret
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped noclasses & $ret
Definition: hooks.txt:1530
UploadBase\OK
const OK
Definition: UploadBase.php:50
UploadFromChunks\performUpload
performUpload( $comment, $pageText, $watch, $user)
Perform the upload, then remove the temp copy afterward.
Definition: UploadFromChunks.php:174
UploadBase\$mLocalFile
$mLocalFile
Definition: UploadBase.php:43
MWException
MediaWiki exception.
Definition: MWException.php:26
UploadBase\getVerificationErrorCode
getVerificationErrorCode( $error)
Definition: UploadBase.php:72
UploadChunkFileException
Definition: UploadFromChunks.php:368
UploadFromChunks\addChunk
addChunk( $chunkPath, $chunkSize, $offset)
Add a chunk to the temporary directory.
Definition: UploadFromChunks.php:201
array
the array() calling protocol came about after MediaWiki 1.4rc1.
List of Api Query prop modules.
$comment
$comment
Definition: importImages.php:107
UploadFromChunks\$mChunkIndex
$mChunkIndex
Definition: UploadFromChunks.php:32
UploadFromChunks\$mOffset
$mOffset
Definition: UploadFromChunks.php:31
UploadBase\$mTempPath
$mTempPath
Definition: UploadBase.php:39
wfDebug
wfDebug( $text, $dest='all')
Sends a line to the debug log if enabled or, optionally, to a comment in output.
Definition: GlobalFunctions.php:933
user
This document is intended to provide useful advice for parties seeking to redistribute MediaWiki to end users It s targeted particularly at maintainers for Linux since it s been observed that distribution packages of MediaWiki often break We ve consistently had to recommend that users seeking support use official tarballs instead of their distribution s and this often solves whatever problem the user is having It would be nice if this could such and we might be restricted by PHP settings such as safe mode or open_basedir We cannot assume that the software even has read access anywhere useful Many shared hosts run all users web applications under the same user
Definition: distributors.txt:9
$name
Allows to change the fields on the form that will be generated $name
Definition: hooks.txt:336
TempFSFile\factory
static factory( $prefix, $extension='')
Make a new temporary file on the file system.
Definition: TempFSFile.php:44
UploadFromChunks\concatenateChunks
concatenateChunks()
Append the final chunk and ready file for parent::performUpload()
Definition: UploadFromChunks.php:113
UploadFromChunks\stashFile
stashFile(User $user=null)
Calls the parent stashFile and updates the uploadsession table to handle "chunks".
Definition: UploadFromChunks.php:72
UploadFromChunks\getChunkStatus
getChunkStatus()
Get the chunk db state and populate update relevant local values.
Definition: UploadFromChunks.php:262
UploadFromChunks\getChunkFileKey
getChunkFileKey( $index=null)
Definition: UploadFromChunks.php:339
UploadBase\verifyPartialFile
verifyPartialFile()
A verification routine suitable for partial files.
Definition: UploadBase.php:453
$user
please add to it if you re going to add events to the MediaWiki code where normally authentication against an external auth plugin would be creating a account $user
Definition: hooks.txt:237
UploadBase\getMaxUploadSize
static getMaxUploadSize( $forType=null)
Definition: UploadBase.php:1719
UploadFromChunks\$mFileKey
$mFileKey
Definition: UploadFromChunks.php:33
UploadBase\initializePathInfo
initializePathInfo( $name, $tempPath, $fileSize, $removeTempFile=false)
Initialize the path information.
Definition: UploadBase.php:202
UploadFromChunks\getVirtualChunkLocation
getVirtualChunkLocation( $index)
Returns the virtual chunk location:
Definition: UploadFromChunks.php:184
$ext
$ext
Definition: NoLocalSettings.php:34
FileBackend\extensionFromPath
static extensionFromPath( $path)
Get the final extension from a storage or FS path.
Definition: FileBackend.php:1400
UploadFromFile\verifyUpload
verifyUpload()
Definition: UploadFromFile.php:80
UploadFromChunks\continueChunks
continueChunks( $name, $key, $webRequestUpload)
Continue chunk uploading.
Definition: UploadFromChunks.php:95
FileRepo\DELETE_SOURCE
const DELETE_SOURCE
Definition: FileRepo.php:38
UploadBase\$mDesiredDestName
$mDesiredDestName
Definition: UploadBase.php:40
UploadFromChunks
Implements uploading from chunks.
Definition: UploadFromChunks.php:30
$error
usually copyright or history_copyright This message must be in HTML not wikitext $subpages will be ignored and the rest of subPageSubtitle() will run. 'SkinTemplateBuildNavUrlsNav_urlsAfterPermalink' whether MediaWiki currently thinks this is a CSS JS page Hooks may change this value to override the return value of Title::isCssOrJsPage(). 'TitleIsAlwaysKnown' whether MediaWiki currently thinks this page is known isMovable() always returns false. $title whether MediaWiki currently thinks this page is movable Hooks may change this value to override the return value of Title::isMovable(). 'TitleIsWikitextPage' whether MediaWiki currently thinks this is a wikitext page Hooks may change this value to override the return value of Title::isWikitextPage() 'TitleMove' use UploadVerification and UploadVerifyFile instead where the first element is the message key and the remaining elements are used as parameters to the message based on mime etc Preferred in most cases over UploadVerification object with all info about the upload string as detected by MediaWiki Handlers will typically only apply for specific mime types object & $error
Definition: hooks.txt:2573
$e
if( $useReadline) $e
Definition: eval.php:66
User
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
Definition: User.php:59
$res
$res
Definition: database.txt:21
UploadFromFile
Implements regular file uploads.
Definition: UploadFromFile.php:30
Status\newFatal
static newFatal( $message)
Factory function for fatal errors.
Definition: Status.php:63
UploadChunkVerificationException
Definition: UploadFromChunks.php:371
UploadFromChunks\verifyChunk
verifyChunk()
Verify that the chunk isn't really an evil html file.
Definition: UploadFromChunks.php:351