Merge branch 'chunked_upload'
This commit is contained in:
commit
e36e00dc00
|
@ -0,0 +1,64 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2012 Bart Visscher <bartv@thisnet.nl>
|
||||||
|
* This file is licensed under the Affero General Public License version 3 or
|
||||||
|
* later.
|
||||||
|
* See the COPYING-README file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* filesync can be called with a PUT method.
|
||||||
|
* PUT takes a stream starting with a 2 byte blocksize,
|
||||||
|
* followed by binary md5 of the blocks. Everything in big-endian.
|
||||||
|
* The return is a json encoded with:
|
||||||
|
* - 'transferid'
|
||||||
|
* - 'needed' chunks
|
||||||
|
* - 'last' checked chunk
|
||||||
|
* The URL is made of 3 parts, the service url (remote.php/filesync/), the sync
|
||||||
|
* type and the path in ownCloud.
|
||||||
|
* At the moment the only supported sync type is 'oc_chunked'.
|
||||||
|
* The final URL will look like http://.../remote.php/filesync/oc_chunked/path/to/file
|
||||||
|
*/
|
||||||
|
|
||||||
|
// only need filesystem apps
|
||||||
|
$RUNTIME_APPTYPES=array('filesystem','authentication');
|
||||||
|
OC_App::loadApps($RUNTIME_APPTYPES);
|
||||||
|
if(!OC_User::isLoggedIn()){
|
||||||
|
if(!isset($_SERVER['PHP_AUTH_USER'])){
|
||||||
|
header('WWW-Authenticate: Basic realm="ownCloud Server"');
|
||||||
|
header('HTTP/1.0 401 Unauthorized');
|
||||||
|
echo 'Valid credentials must be supplied';
|
||||||
|
exit();
|
||||||
|
} else {
|
||||||
|
if(!OC_User::login($_SERVER["PHP_AUTH_USER"], $_SERVER["PHP_AUTH_PW"])){
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
list($type,$file) = explode('/', substr($path_info,1+strlen($service)+1), 2);
|
||||||
|
|
||||||
|
if ($type != 'oc_chunked') {
|
||||||
|
OC_Response::setStatus(OC_Response::STATUS_NOT_FOUND);
|
||||||
|
die;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!OC_Filesystem::is_file($file)) {
|
||||||
|
OC_Response::setStatus(OC_Response::STATUS_NOT_FOUND);
|
||||||
|
die;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch($_SERVER['REQUEST_METHOD']) {
|
||||||
|
case 'PUT':
|
||||||
|
$input = fopen("php://input", "r");
|
||||||
|
$org_file = OC_Filesystem::fopen($file, 'rb');
|
||||||
|
$info = array(
|
||||||
|
'name' => basename($file),
|
||||||
|
);
|
||||||
|
$sync = new OC_FileChunking($info);
|
||||||
|
$result = $sync->signature_split($org_file, $input);
|
||||||
|
echo json_encode($result);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
OC_Response::setStatus(OC_Response::STATUS_NOT_FOUND);
|
||||||
|
}
|
|
@ -15,5 +15,6 @@
|
||||||
<remote>
|
<remote>
|
||||||
<files>appinfo/remote.php</files>
|
<files>appinfo/remote.php</files>
|
||||||
<webdav>appinfo/remote.php</webdav>
|
<webdav>appinfo/remote.php</webdav>
|
||||||
|
<filesync>appinfo/filesync.php</filesync>
|
||||||
</remote>
|
</remote>
|
||||||
</info>
|
</info>
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
1.1.4
|
1.1.5
|
||||||
|
|
|
@ -48,11 +48,23 @@ class OC_Connector_Sabre_Directory extends OC_Connector_Sabre_Node implements Sa
|
||||||
* @return null|string
|
* @return null|string
|
||||||
*/
|
*/
|
||||||
public function createFile($name, $data = null) {
|
public function createFile($name, $data = null) {
|
||||||
|
if (isset($_SERVER['HTTP_OC_CHUNKED'])) {
|
||||||
|
$info = OC_FileChunking::decodeName($name);
|
||||||
|
$chunk_handler = new OC_FileChunking($info);
|
||||||
|
$chunk_handler->store($info['index'], $data);
|
||||||
|
if ($chunk_handler->isComplete()) {
|
||||||
|
$newPath = $this->path . '/' . $info['name'];
|
||||||
|
$f = OC_Filesystem::fopen($newPath, 'w');
|
||||||
|
$chunk_handler->assemble($f);
|
||||||
|
return OC_Connector_Sabre_Node::getETagPropertyForPath($newPath);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$newPath = $this->path . '/' . $name;
|
||||||
|
OC_Filesystem::file_put_contents($newPath,$data);
|
||||||
|
return OC_Connector_Sabre_Node::getETagPropertyForPath($newPath);
|
||||||
|
}
|
||||||
|
|
||||||
$newPath = $this->path . '/' . $name;
|
return null;
|
||||||
OC_Filesystem::file_put_contents($newPath,$data);
|
|
||||||
|
|
||||||
return OC_Connector_Sabre_Node::getETagPropertyForPath($newPath);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -0,0 +1,94 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2012 Bart Visscher <bartv@thisnet.nl>
|
||||||
|
* This file is licensed under the Affero General Public License version 3 or
|
||||||
|
* later.
|
||||||
|
* See the COPYING-README file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
class OC_FileChunking {
|
||||||
|
protected $info;
|
||||||
|
protected $cache;
|
||||||
|
|
||||||
|
static public function decodeName($name) {
|
||||||
|
preg_match('/(?P<name>.*)-chunking-(?P<transferid>\d+)-(?P<chunkcount>\d+)-(?P<index>\d+)/', $name, $matches);
|
||||||
|
return $matches;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __construct($info) {
|
||||||
|
$this->info = $info;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPrefix() {
|
||||||
|
$name = $this->info['name'];
|
||||||
|
$transferid = $this->info['transferid'];
|
||||||
|
|
||||||
|
return $name.'-chunking-'.$transferid.'-';
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getCache() {
|
||||||
|
if (!isset($this->cache)) {
|
||||||
|
$this->cache = new OC_Cache_File();
|
||||||
|
}
|
||||||
|
return $this->cache;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function store($index, $data) {
|
||||||
|
$cache = $this->getCache();
|
||||||
|
$name = $this->getPrefix().$index;
|
||||||
|
$cache->set($name, $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isComplete() {
|
||||||
|
$prefix = $this->getPrefix();
|
||||||
|
$parts = 0;
|
||||||
|
$cache = $this->getCache();
|
||||||
|
for($i=0; $i < $this->info['chunkcount']; $i++) {
|
||||||
|
if ($cache->hasKey($prefix.$i)) {
|
||||||
|
$parts ++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $parts == $this->info['chunkcount'];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function assemble($f) {
|
||||||
|
$cache = $this->getCache();
|
||||||
|
$prefix = $this->getPrefix();
|
||||||
|
for($i=0; $i < $this->info['chunkcount']; $i++) {
|
||||||
|
$chunk = $cache->get($prefix.$i);
|
||||||
|
$cache->remove($prefix.$i);
|
||||||
|
fwrite($f,$chunk);
|
||||||
|
}
|
||||||
|
fclose($f);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function signature_split($orgfile, $input) {
|
||||||
|
$info = unpack('n', fread($input, 2));
|
||||||
|
$blocksize = $info[1];
|
||||||
|
$this->info['transferid'] = mt_rand();
|
||||||
|
$count = 0;
|
||||||
|
$needed = array();
|
||||||
|
$cache = $this->getCache();
|
||||||
|
$prefix = $this->getPrefix();
|
||||||
|
while (!feof($orgfile)) {
|
||||||
|
$new_md5 = fread($input, 16);
|
||||||
|
if (feof($input)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
$data = fread($orgfile, $blocksize);
|
||||||
|
$org_md5 = md5($data, true);
|
||||||
|
if ($org_md5 == $new_md5) {
|
||||||
|
$cache->set($prefix.$count, $data);
|
||||||
|
} else {
|
||||||
|
$needed[] = $count;
|
||||||
|
}
|
||||||
|
$count++;
|
||||||
|
}
|
||||||
|
return array(
|
||||||
|
'transferid' => $this->info['transferid'],
|
||||||
|
'needed' => $needed,
|
||||||
|
'count' => $count,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue