* Options to $options include:
* - headers : name/value map of HTTP headers to use in response to GET/HEAD requests
*
- * @param string $srcPath The source file system path, storage path, or URL
+ * @param string|FSFile $src The source file system path, storage path, or URL
* @param string $dstRel The destination relative path
* @param string $archiveRel The relative path where the existing file is to
* be archived, if there is one. Relative to the public zone root.
* @return FileRepoStatus
*/
public function publish(
- $srcPath, $dstRel, $archiveRel, $flags = 0, array $options = []
+ $src, $dstRel, $archiveRel, $flags = 0, array $options = []
) {
$this->assertWritableRepo(); // fail out if read-only
$status = $this->publishBatch(
- [ [ $srcPath, $dstRel, $archiveRel, $options ] ], $flags );
+ [ [ $src, $dstRel, $archiveRel, $options ] ], $flags );
if ( $status->successCount == 0 ) {
$status->ok = false;
}
$sourceFSFilesToDelete = []; // cleanup for disk source files
// Validate each triplet and get the store operation...
foreach ( $ntuples as $ntuple ) {
- list( $srcPath, $dstRel, $archiveRel ) = $ntuple;
+ list( $src, $dstRel, $archiveRel ) = $ntuple;
+ $srcPath = ( $src instanceof FSFile ) ? $src->getPath() : $src;
+
$options = isset( $ntuple[3] ) ? $ntuple[3] : [];
// Resolve source to a storage path if virtual
$srcPath = $this->resolveToStoragePath( $srcPath );
} else { // FS source path
$operations[] = [
'op' => 'store',
- 'src' => $srcPath,
+ 'src' => $src, // prefer FSFile objects
'dst' => $dstPath,
'overwrite' => true, // replace current
'headers' => $headers
}
public function publish(
- $srcPath,
+ $src,
$dstRel,
$archiveRel,
$flags = 0,
* Options to $options include:
* - headers : name/value map of HTTP headers to use in response to GET/HEAD requests
*
- * @param string $srcPath Local filesystem path to the source image
+ * @param string|FSFile $src Local filesystem path to the source image
* @param int $flags A bitwise combination of:
* File::DELETE_SOURCE Delete the source file, i.e. move rather than copy
* @param array $options Optional additional parameters
* STUB
* Overridden by LocalFile
*/
- function publish( $srcPath, $flags = 0, array $options = [] ) {
+ function publish( $src, $flags = 0, array $options = [] ) {
$this->readOnlyError();
}
/**
* Upload a file and record it in the DB
- * @param string $srcPath Source storage path, virtual URL, or filesystem path
+ * @param string|FSFile $src Source storage path, virtual URL, or filesystem path
* @param string $comment Upload description
* @param string $pageText Text to use for the new description page,
* if a new description page is created
* @return FileRepoStatus On success, the value member contains the
* archive name, or an empty string if it was a new file.
*/
- function upload( $srcPath, $comment, $pageText, $flags = 0, $props = false,
+ function upload( $src, $comment, $pageText, $flags = 0, $props = false,
$timestamp = false, $user = null, $tags = []
) {
global $wgContLang;
return $this->readOnlyFatalStatus();
}
+ $srcPath = ( $src instanceof FSFile ) ? $src->getPath() : $src;
if ( !$props ) {
if ( $this->repo->isVirtualUrl( $srcPath )
|| FileBackend::isStoragePath( $srcPath )
// non-nicely (dangling multi-byte chars, non-truncated version in cache).
$comment = $wgContLang->truncate( $comment, 255 );
$this->lock(); // begin
- $status = $this->publish( $srcPath, $flags, $options );
+ $status = $this->publish( $src, $flags, $options );
if ( $status->successCount >= 2 ) {
// There will be a copy+(one of move,copy,store).
* The archive name should be passed through to recordUpload for database
* registration.
*
- * @param string $srcPath Local filesystem path or virtual URL to the source image
+ * @param string|FSFile $src Local filesystem path or virtual URL to the source image
* @param int $flags A bitwise combination of:
* File::DELETE_SOURCE Delete the source file, i.e. move rather than copy
* @param array $options Optional additional parameters
* @return FileRepoStatus On success, the value member contains the
* archive name, or an empty string if it was a new file.
*/
- function publish( $srcPath, $flags = 0, array $options = [] ) {
- return $this->publishTo( $srcPath, $this->getRel(), $flags, $options );
+ function publish( $src, $flags = 0, array $options = [] ) {
+ return $this->publishTo( $src, $this->getRel(), $flags, $options );
}
/**
* The archive name should be passed through to recordUpload for database
* registration.
*
- * @param string $srcPath Local filesystem path or virtual URL to the source image
+ * @param string|FSFile $src Local filesystem path or virtual URL to the source image
* @param string $dstRel Target relative path
* @param int $flags A bitwise combination of:
* File::DELETE_SOURCE Delete the source file, i.e. move rather than copy
* @return FileRepoStatus On success, the value member contains the
* archive name, or an empty string if it was a new file.
*/
- function publishTo( $srcPath, $dstRel, $flags = 0, array $options = [] ) {
+ function publishTo( $src, $dstRel, $flags = 0, array $options = [] ) {
+ $srcPath = ( $src instanceof FSFile ) ? $src->getPath() : $src;
+
$repo = $this->getRepo();
if ( $repo->getReadOnlyReason() !== false ) {
return $this->readOnlyFatalStatus();
if ( $repo->hasSha1Storage() ) {
$sha1 = $repo->isVirtualUrl( $srcPath )
? $repo->getFileSha1( $srcPath )
- : File::sha1Base36( $srcPath );
+ : FSFile::getSha1Base36FromPath( $srcPath );
$dst = $repo->getBackend()->getPathForSHA1( $sha1 );
- $status = $repo->quickImport( $srcPath, $dst );
+ $status = $repo->quickImport( $src, $dst );
if ( $flags & File::DELETE_SOURCE ) {
unlink( $srcPath );
}
* @author Michael Dale
*/
abstract class UploadBase {
+ /** @var string Local file system path to the file to upload (or a local copy) */
protected $mTempPath;
+ /** @var TempFSFile|null Wrapper to handle deleting the temp file */
+ protected $tempFileObj;
+
protected $mDesiredDestName, $mDestName, $mRemoveTempFile, $mSourceType;
protected $mTitle = false, $mTitleError = 0;
protected $mFilteredName, $mFinalExtension;
if ( FileBackend::isStoragePath( $tempPath ) ) {
throw new MWException( __METHOD__ . " given storage path `$tempPath`." );
}
- $this->mTempPath = $tempPath;
- $this->mFileSize = $fileSize;
+
+ $this->setTempFile( $tempPath, $fileSize );
$this->mRemoveTempFile = $removeTempFile;
}
*/
abstract public function initializeFromRequest( &$request );
+ /**
+ * @param string $tempPath File system path to temporary file containing the upload
+ * @param integer $fileSize
+ */
+ protected function setTempFile( $tempPath, $fileSize = null ) {
+ $this->mTempPath = $tempPath;
+ if ( strlen( $this->mTempPath ) && file_exists( $this->mTempPath ) ) {
+ $this->tempFileObj = new TempFSFile( $this->mTempPath );
+ $this->mFileSize = $fileSize ?: filesize( $this->mTempPath );
+ } else {
+ $this->tempFileObj = null;
+ $this->mFileSize = null;
+ }
+ }
+
/**
* Fetch the file. Usually a no-op
* @return Status
* on exit to clean up.
*/
public function cleanupTempFile() {
- if ( $this->mRemoveTempFile && $this->mTempPath && file_exists( $this->mTempPath ) ) {
- wfDebug( __METHOD__ . ": Removing temporary file {$this->mTempPath}\n" );
- unlink( $this->mTempPath );
+ if ( $this->mRemoveTempFile && $this->tempFileObj ) {
+ // Delete when all relevant TempFSFile handles go out of scope
+ wfDebug( __METHOD__ . ": Marked temporary file '{$this->mTempPath}' for removal\n" );
+ $this->tempFileObj->autocollect();
}
}
}
wfDebugLog( 'fileconcatenate', "Combined $i chunks in $tAmount seconds." );
- // File system path
- $this->mTempPath = $tmpPath;
- // Since this was set for the last chunk previously
- $this->mFileSize = filesize( $this->mTempPath );
+ // File system path of the actual full temp file
+ $this->setTempFile( $tmpPath );
+
$ret = $this->verifyUpload();
if ( $ret['status'] !== UploadBase::OK ) {
wfDebugLog( 'fileconcatenate', "Verification failed for chunked upload" );