Add support for partially uploaded files

Summary:
Ref T7149. This flags allocated but incomplete files and doesn't explode when trying to download them.

Files are marked complete when the last chunk is uploaded.

I added a key on `<authorPHID, isPartial>` so we can show you a list of partially uploaded files and prompt you to resume them at some point down the road.

Test Plan: Massaged debugging settings and uploaded README.md very slowly in 32b chunks. Saw the file lose its "Partial" flag when the last chunk finished.

Reviewers: btrahan

Reviewed By: btrahan

Subscribers: joshuaspence, epriestley

Maniphest Tasks: T7149

Differential Revision: https://secure.phabricator.com/D12063
This commit is contained in:
epriestley
2015-03-13 11:30:24 -07:00
parent 6c3552f939
commit aa4adf3ab8
9 changed files with 136 additions and 58 deletions

View File

@@ -5,6 +5,7 @@ final class PhabricatorFileDataController extends PhabricatorFileController {
private $phid;
private $key;
private $token;
private $file;
public function willProcessRequest(array $data) {
$this->phid = $data['phid'];
@@ -16,20 +17,9 @@ final class PhabricatorFileDataController extends PhabricatorFileController {
return false;
}
protected function checkFileAndToken($file) {
if (!$file) {
return new Aphront404Response();
}
if (!$file->validateSecretKey($this->key)) {
return new Aphront403Response();
}
return null;
}
public function processRequest() {
$request = $this->getRequest();
$viewer = $this->getViewer();
$alt = PhabricatorEnv::getEnvConfig('security.alternate-file-domain');
$base_uri = PhabricatorEnv::getEnvConfig('phabricator.base-uri');
@@ -44,32 +34,22 @@ final class PhabricatorFileDataController extends PhabricatorFileController {
// Alternate files domain isn't configured or it's set
// to the same as the default domain
// load the file with permissions checks;
$file = id(new PhabricatorFileQuery())
->setViewer($request->getUser())
->withPHIDs(array($this->phid))
->executeOne();
$error_response = $this->checkFileAndToken($file);
if ($error_response) {
return $error_response;
$response = $this->loadFile($viewer);
if ($response) {
return $response;
}
$file = $this->getFile();
// when the file is not CDNable, don't allow cache
$cache_response = $file->getCanCDN();
} else if ($req_domain != $alt_domain) {
// Alternate domain is configured but this request isn't using it
// load the file with permissions checks;
$file = id(new PhabricatorFileQuery())
->setViewer($request->getUser())
->withPHIDs(array($this->phid))
->executeOne();
$error_response = $this->checkFileAndToken($file);
if ($error_response) {
return $error_response;
$response = $this->loadFile($viewer);
if ($response) {
return $response;
}
$file = $this->getFile();
// if the user can see the file, generate a token;
// redirect to the alt domain with the token;
@@ -82,18 +62,15 @@ final class PhabricatorFileDataController extends PhabricatorFileController {
->setURI($token_uri);
} else {
// We are using the alternate domain
// We are using the alternate domain. We don't have authentication
// on this domain, so we bypass policy checks when loading the file.
// load the file, bypassing permission checks;
$file = id(new PhabricatorFileQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withPHIDs(array($this->phid))
->executeOne();
$error_response = $this->checkFileAndToken($file);
if ($error_response) {
return $error_response;
$bypass_policies = PhabricatorUser::getOmnipotentUser();
$response = $this->loadFile($bypass_policies);
if ($response) {
return $response;
}
$file = $this->getFile();
$acquire_token_uri = id(new PhutilURI($file->getViewURI()))
->setDomain($main_domain);
@@ -205,4 +182,44 @@ final class PhabricatorFileDataController extends PhabricatorFileController {
return $uri;
}
private function loadFile(PhabricatorUser $viewer) {
$file = id(new PhabricatorFileQuery())
->setViewer($viewer)
->withPHIDs(array($this->phid))
->executeOne();
if (!$file) {
return new Aphront404Response();
}
if (!$file->validateSecretKey($this->key)) {
return new Aphront403Response();
}
if ($file->getIsPartial()) {
// We may be on the CDN domain, so we need to use a fully-qualified URI
// here to make sure we end up back on the main domain.
$info_uri = PhabricatorEnv::getURI($file->getInfoURI());
return $this->newDialog()
->setTitle(pht('Partial Upload'))
->appendParagraph(
pht(
'This file has only been partially uploaded. It must be '.
'uploaded completely before you can download it.'))
->addCancelButton($info_uri);
}
$this->file = $file;
return null;
}
private function getFile() {
if (!$this->file) {
throw new Exception(pht('Call loadFile() before getFile()!'));
}
return $this->file;
}
}