localtos3.php 39 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938
  1. <?php
  2. putenv('LDAPTLS_CACERT=/etc/ssl/certs/cacism3.pem');
  3. /* *********************************************************************************** */
  4. /* 2023 code created by Eesger Toering / knoop.frl / geoarchive.eu */
  5. /* GitHub: https://github.com/mrAceT/nextcloud-S3-local-S3-migration */
  6. /* Like the work? You'll be surprised how much time goes into things like this.. */
  7. /* be my hero, support my work, */
  8. /* https://paypal.me/eesgertoering */
  9. /* https://www.geef.nl/en/donate?action=15544 */
  10. /* *********************************************************************************** */
  11. # best practice: run the script as the cloud-user!!
  12. # sudo -u apache php81 -d memory_limit=1024M /var/www/html/nextcloud/localtos3.php
  13. # runuser -u apache -- composer require aws/aws-sdk-php
  14. use Aws\S3\S3Client;
  15. # uncomment this for large file uploads (Amazon advises this voor 100Mb+ files)
  16. use Aws\Exception\AwsException;
  17. use Aws\S3\ObjectUploader;
  18. use Aws\S3\MultipartUploader;
  19. use Aws\Exception\MultipartUploadException;
  20. $MULTIPART_THRESHOLD = 1536; #Megabytes
  21. $MULTIPART_CONCURRENCY = 3;
  22. echo "\n#########################################################################################".
  23. "\n Migration tool for Nextcloud local to S3 version 0.35".
  24. "\n".
  25. "\n Reading config...";
  26. $PREVIEW_MAX_AGE = 0; // max age (days) of preview images (EXPERIMENTAL! 0 = no del)
  27. $PREVIEW_MAX_DEL = 0.005; // max amount of previews to delete at a time (when < 1 & > 0 => percentage! )..
  28. // Note: Preferably use absolute path without trailing directory separators
  29. $PATH_BASE = '/var/www/html/nextcloud'; // Path to the base of the main Nextcloud directory
  30. $PATH_NEXTCLOUD = $PATH_BASE; // Path of the public Nextcloud directory
  31. $PATH_BACKUP = $PATH_BASE.'/bak'; // Path for backup of MySQL database (you must create it yourself..)
  32. $OCC_BASE = 'php -d memory_limit=2048M '.$PATH_NEXTCLOUD.'/occ ';
  33. // don't forget this one --. (if you don't run the script as the 'clouduser', see first comment at the top)
  34. #$OCC_BASE = 'sudo -u apache php81 -d memory_limit=1024M '.$PATH_NEXTCLOUD.'/occ ';
  35. $TEST = '2'; //'admin';//'appdata_ocspss2ph00r';
  36. // set to 0 for LIVE!!
  37. // set to 1 for all data : NO db modifications, with file modifications/uplaods/removal
  38. // set to user name for single user (migration) test
  39. // set to 2 for complete dry run
  40. $SET_MAINTENANCE = 0; // only in $TEST=0 Nextcloud will be put into maintenance mode
  41. // ONLY when migration is all done you can set this to 0 for the S3-consitancy checks
  42. $SHOWINFO = 1; // set to 0 to force much less info (while testing)
  43. $SQL_DUMP_USER = ''; // leave both empty if nextcloud user has enough rights..
  44. $SQL_DUMP_PASS = '';
  45. $CONFIG_OBJECTSTORE = dirname(__FILE__).'/storage.config.php';
  46. # It is probably wise to set the two vars below to '1' once, let the 'Nextcloud' do some checking..
  47. $DO_FILES_CLEAN = 0; // perform occ files:cleanup | can take a while on large accounts (should not be necessary but cannot hurt / not working while in maintenance.. )
  48. $DO_FILES_SCAN = 0; // perform occ files:scan --all | can take a while on large accounts (should not be necessary but cannot hurt / not working while in maintenance.. )
  49. echo "\n".
  50. "\n#########################################################################################".
  51. "\nSetting up local migration to S3 (sync)...\n";
  52. // Autoload
  53. require_once(dirname(__FILE__).'/3rdparty/autoload.php');
  54. echo "\nfirst load the nextcloud config...";
  55. include($PATH_NEXTCLOUD.'/config/config.php');
  56. if (!empty($CONFIG['objectstore'])) {
  57. if ($CONFIG_OBJECTSTORE == $PATH_NEXTCLOUD.'/config/config.php') {
  58. echo "\nS3 config found in \$PATH_NEXTCLOUD system config.php => same as \$CONFIG_OBJECTSTORE !";
  59. } else {
  60. echo "\nS3 config found in \$PATH_NEXTCLOUD system config.php => \$CONFIG_OBJECTSTORE not used! ($CONFIG_OBJECTSTORE)";
  61. }
  62. $CONFIG_OBJECTSTORE = ''; //no copy!
  63. } else {
  64. echo "\nS3 NOT configured in config.php, using \$CONFIG_OBJECTSTORE";
  65. if (is_string($CONFIG_OBJECTSTORE) && file_exists($CONFIG_OBJECTSTORE)) {
  66. $CONFIG_MERGE = $CONFIG;
  67. include($CONFIG_OBJECTSTORE);
  68. $CONFIG = array_merge($CONFIG_MERGE,$CONFIG);
  69. }
  70. else if (is_array($CONFIG_OBJECTSTORE)) {
  71. $CONFIG['objectstore'] = $CONFIG_OBJECTSTORE;
  72. } else {
  73. echo "\nERROR: var \$CONFIG_OBJECTSTORE is not configured (".gettype($CONFIG_OBJECTSTORE)." / $CONFIG_OBJECTSTORE)\n\n";
  74. die;
  75. }
  76. }
  77. $PATH_DATA = preg_replace('/\/*$/','',$CONFIG['datadirectory']);
  78. echo "\nconnect to sql-database...";
  79. // Database setup
  80. $mysqli = new mysqli($CONFIG['dbhost'], $CONFIG['dbuser'], $CONFIG['dbpassword'], $CONFIG['dbname']);
  81. if ($CONFIG['mysql.utf8mb4']) {
  82. $mysqli->set_charset('utf8mb4');
  83. }
  84. ################################################################################ checks #
  85. $LOCAL_STORE_ID = 0;
  86. if ($result = $mysqli->query("SELECT * FROM `oc_storages` WHERE `id` = 'local::$PATH_DATA/'")) {
  87. if ($result->num_rows>1) {
  88. echo "\nERROR: Multiple 'local::$PATH_DATA', it's an accident waiting to happen!!\n";
  89. die;
  90. }
  91. else if ($result->num_rows == 1) {
  92. echo "\nFOUND 'local::$PATH_DATA', good. ";
  93. $row = $result->fetch_assoc();
  94. $LOCAL_STORE_ID = $row['numeric_id']; // for creative rename command..
  95. echo "\nThe local store id is:$LOCAL_STORE_ID";
  96. } else {
  97. echo "\nWARNING: no 'local::$PATH_DATA' found, therefor no sync local data > S3!\n";
  98. }
  99. }
  100. $OBJECT_STORE_ID = 0;
  101. if ($result = $mysqli->query("SELECT * FROM `oc_storages` WHERE `id` LIKE 'object::store:amazon::".$CONFIG['objectstore']['arguments']['bucket']."'")) {
  102. if ($result->num_rows>1) {
  103. echo "\nMultiple 'object::store:amazon::".$CONFIG['objectstore']['arguments']['bucket']."' clean this up, it's an accident waiting to happen!!\n\n";
  104. die;
  105. }
  106. else if ($result->num_rows == 0) {
  107. if (empty($CONFIG['objectstore'])) {
  108. echo "\nERROR: No 'object::store:' & NO S3 storage defined\n\n";
  109. die;
  110. } else {
  111. echo "\nNOTE: No 'object::store:' > S3 storage = defined\n\n";
  112. echo "\n Upon migration local will be renamed to object::store";
  113. }
  114. }
  115. else {
  116. echo "\nFOUND 'object::store:amazon::".$CONFIG['objectstore']['arguments']['bucket']."', OK";
  117. $row = $result->fetch_assoc();
  118. $OBJECT_STORE_ID = $row['numeric_id']; // for creative rename command..
  119. echo "\nThe object store id is:$OBJECT_STORE_ID";
  120. $result = $mysqli->query("SELECT `fileid` FROM `oc_filecache` WHERE `storage` = ".$OBJECT_STORE_ID);
  121. if ( $result->num_rows > 0 ) {
  122. echo "\n\nWARNING: if this is for a full migration remove all data with `storage` = $OBJECT_STORE_ID in your `oc_filecache` !!!!\n";
  123. }
  124. }
  125. }
  126. $result->free_result();
  127. echo "\n".
  128. "\n######################################################################################### ".$TEST;
  129. if (empty($TEST) ) {
  130. echo "\n\nNOTE: THIS IS THE REAL THING!!\n";
  131. } else {
  132. echo empty($TEST) ? '' : "\nWARNING: you are in test mode (".$TEST.")";
  133. }
  134. echo "\nBase init complete, continue?";
  135. $getLine = '';
  136. while ($getLine == ''): $getLine = fgets( fopen("php://stdin","r") ); endwhile;
  137. echo "\n######################################################################################### ";
  138. if ($DO_FILES_CLEAN) {
  139. echo "\nRunning cleanup (should not be necessary but cannot hurt)";
  140. echo occ($OCC_BASE,'files:cleanup');
  141. }
  142. if ($DO_FILES_SCAN) {
  143. echo "\nRunning scan (should not be necessary but cannot hurt)";
  144. echo occ($OCC_BASE,'files:scan --all');
  145. }
  146. if (empty($TEST)) {
  147. if ($SET_MAINTENANCE) { // maintenance mode
  148. $process = occ($OCC_BASE,'maintenance:mode --on');
  149. echo $process;
  150. if (strpos($process, "\nMaintenance mode") == 0
  151. && strpos($process, 'Maintenance mode already enabled') == 0) {
  152. echo " could not set.. ouput command: ".$process."\n\n";
  153. die;
  154. }
  155. }
  156. } else {
  157. echo "\n\nNOTE: In TEST-mode, will not enter maintenance mode";
  158. }
  159. echo "\ndatabase backup...";
  160. if (!is_dir($PATH_BACKUP)) { echo "\$PATH_BACKUP folder does not exist\n"; die; }
  161. [$host_clear, $host_port] = explode(':',$CONFIG['dbhost']);
  162. $process = shell_exec('mysqldump --host='.$host_clear.
  163. ' --user='.(empty($SQL_DUMP_USER)?$CONFIG['dbuser']:$SQL_DUMP_USER).
  164. ' --password='.escapeshellcmd( empty($SQL_DUMP_PASS)?$CONFIG['dbpassword']:$SQL_DUMP_PASS ).' '.$CONFIG['dbname'].
  165. ' > '.$PATH_BACKUP . DIRECTORY_SEPARATOR . 'backup.sql');
  166. if (strpos(' '.strtolower($process), 'error:') > 0) {
  167. echo "sql dump error\n";
  168. die;
  169. } else {
  170. echo "\n(to restore: mysql -u ".(empty($SQL_DUMP_USER)?$CONFIG['dbuser']:$SQL_DUMP_USER)." -p ".$CONFIG['dbname']." < backup.sql)\n";
  171. }
  172. echo "\nbackup config.php...";
  173. $copy = 1;
  174. if(file_exists($PATH_BACKUP.'/config.php')){
  175. if (filemtime($PATH_NEXTCLOUD.'/config/config.php') > filemtime($PATH_BACKUP.'/config.php') ) {
  176. unlink($PATH_BACKUP.'/config.php');
  177. }
  178. else {
  179. echo 'not needed';
  180. $copy = 0;
  181. }
  182. }
  183. if ($copy) {
  184. copy($PATH_NEXTCLOUD.'/config/config.php', $PATH_BACKUP.'/config.php');
  185. }
  186. echo "\nconnect to S3...";
  187. $bucket = $CONFIG['objectstore']['arguments']['bucket'];
  188. $s3 = new S3Client([
  189. 'version' => 'latest',
  190. //'endpoint' => 'https://'.$bucket.'.'.$CONFIG['objectstore']['arguments']['hostname'],
  191. 'endpoint' => $CONFIG['objectstore']['arguments']['hostname'] . ':' . $CONFIG['objectstore']['arguments']['port'],
  192. //'bucket' => $bucket,
  193. //'bucket_endpoint' => true,
  194. 'region' => $CONFIG['objectstore']['arguments']['region'],
  195. 'credentials' => [
  196. 'key' => $CONFIG['objectstore']['arguments']['key'],
  197. 'secret' => $CONFIG['objectstore']['arguments']['secret'],
  198. ],
  199. 'use_path_style_endpoint' => $CONFIG['objectstore']['arguments']['use_path_style']
  200. ]);
  201. echo "\n".
  202. "\n#########################################################################################".
  203. "\nSetting everything up finished ##########################################################";
  204. echo "\n".
  205. "\n#########################################################################################".
  206. "\nappdata preview size...";
  207. $PREVIEW_MAX_AGEU = 0;
  208. $PREVIEW_1YR_AGEU = 0;
  209. if ($PREVIEW_MAX_AGE > 0) {
  210. echo "\nremove older then ".$PREVIEW_MAX_AGE." day".($PREVIEW_MAX_AGE>1?'s':'');
  211. $PREVIEW_MAX_AGEU = new DateTime(); // For today/now, don't pass an arg.
  212. $PREVIEW_MAX_AGEU->modify("-".$PREVIEW_MAX_AGE." day".($PREVIEW_MAX_AGE>1?'s':''));
  213. echo " > clear before ".$PREVIEW_MAX_AGEU->format( 'd-m-Y' )." (U:".$PREVIEW_MAX_AGEU->format( 'U' ).")";
  214. $PREVIEW_MAX_AGEU = $PREVIEW_MAX_AGEU->format( 'U' );
  215. } else {
  216. echo " (\$PREVIEW_MAX_AGE = 0 days, stats only)";
  217. }
  218. $PREVIEW_1YR_AGEU = new DateTime(); // For today/now, don't pass an arg.
  219. $PREVIEW_1YR_AGEU->modify("-1year");
  220. $PREVIEW_1YR_AGEU = $PREVIEW_1YR_AGEU->format( 'U' );
  221. $PREVIEW_NOW = [0,0];
  222. $PREVIEW_DEL = [0,0];
  223. $PREVIEW_REM = [0,0];
  224. $PREVIEW_1YR = [0,0];
  225. if (!$result = $mysqli->query("SELECT `ST`.`id`, `FC`.`fileid`, `FC`.`path`, `FC`.`size`, `FC`.`storage_mtime` FROM".
  226. " `oc_filecache` as `FC`,".
  227. " `oc_storages` as `ST`,".
  228. " `oc_mimetypes` as `MT`".
  229. " WHERE 1".
  230. " AND `FC`.`path` LIKE 'appdata_%'".
  231. " AND `FC`.`path` LIKE '%/preview/%'".
  232. # " AND `ST`.`id` LIKE 'object::%'".
  233. # " AND `FC`.`fileid` = '".substr($object['Key'],8)."'". # should be only one..
  234. " AND `ST`.`numeric_id` = `FC`.`storage`".
  235. " AND `FC`.`mimetype` = `MT`.`id`".
  236. " AND `MT`.`mimetype` != 'httpd/unix-directory'".
  237. " ORDER BY `FC`.`storage_mtime` ASC")) {
  238. echo "\nERROR: query pos 1";
  239. die;
  240. } else {
  241. if ($PREVIEW_MAX_DEL > 0
  242. && $PREVIEW_MAX_DEL < 1) {
  243. $PREVIEW_MAX_DEL*= $result->num_rows;
  244. }
  245. while ($row = $result->fetch_assoc()) {
  246. // Determine correct path
  247. //echo "\n\nPEDRO_".$row['id']."_PEDRO_".$row['path']."\n\n";
  248. if (substr($row['id'], 0, 13) == 'object::user:') {
  249. $path = $PATH_DATA . DIRECTORY_SEPARATOR . substr($row['id'], 13) . DIRECTORY_SEPARATOR . $row['path'];
  250. }
  251. else if (substr($row['id'], 0, 6) == 'home::') {
  252. $path = $PATH_DATA . DIRECTORY_SEPARATOR . substr($row['id'], 6) . DIRECTORY_SEPARATOR . $row['path'];
  253. } else {
  254. $path = $PATH_DATA . DIRECTORY_SEPARATOR . $row['path'];
  255. }
  256. $user = substr($path, strlen($PATH_DATA. DIRECTORY_SEPARATOR));
  257. $user = substr($user,0,strpos($user,DIRECTORY_SEPARATOR));
  258. if ($PREVIEW_MAX_AGEU > $row['storage_mtime']
  259. && $PREVIEW_MAX_DEL > 1) {
  260. $PREVIEW_MAX_DEL--;
  261. if (empty($TEST)) {
  262. if(file_exists($path) && is_file($path)){
  263. unlink($path);
  264. }
  265. $result_s3 = S3del($s3, $bucket, 'urn:oid:'.$row['fileid']);
  266. $mysqli->query("DELETE FROM `oc_filecache` WHERE `oc_filecache`.`fileid` = ".$row['fileid']);
  267. } else {
  268. echo "\nfileID ".$matches[2]." has a preview older then the set \$PREVIEW_MAX_AGE";
  269. }
  270. $PREVIEW_DEL[1] += $row['size'];
  271. $PREVIEW_DEL[0]++;
  272. } else {
  273. if (preg_match('/\/preview\/([a-f0-9]\/[a-f0-9]\/[a-f0-9]\/[a-f0-9]\/[a-f0-9]\/[a-f0-9]\/[a-f0-9]\/)?([0-9]+)\/[^\/]+$/',$path,$matches)) {
  274. #echo "check fileID".$matches[2].' ';
  275. $result2 = $mysqli->query("SELECT `storage` FROM `oc_filecache` WHERE `oc_filecache`.`fileid` = ".$matches[2]);
  276. if ($result2->num_rows == 0 ) {
  277. if (empty($TEST)) {
  278. if(file_exists($path) && is_file($path)){
  279. unlink($path);
  280. }
  281. $result_s3 = S3del($s3, $bucket, 'urn:oid:'.$row['fileid']);
  282. $mysqli->query("DELETE FROM `oc_filecache` WHERE `oc_filecache`.`fileid` = ".$row['fileid']);
  283. } else {
  284. echo "\nfileID ".$matches[2]." has a preview, but the source file does not exist, would delete the preview (fileID ".$row['fileid'].")";
  285. }
  286. $PREVIEW_REM[0]++;
  287. $PREVIEW_REM[1] += $row['size'];
  288. } else {
  289. if ($PREVIEW_1YR_AGEU > $row['storage_mtime'] ) {
  290. $PREVIEW_1YR[1] += $row['size'];
  291. $PREVIEW_1YR[0]++;
  292. }
  293. $PREVIEW_NOW[1] += $row['size'];
  294. $PREVIEW_NOW[0]++;
  295. }
  296. $result2->free_result();
  297. } else {
  298. echo "\n\nERROR: path format not as expected (".$row['fileid']." : $path)";
  299. echo "\n\tremove the database entry..";
  300. if (empty($TEST)) {
  301. $mysqli->query("DELETE FROM `oc_filecache` WHERE `oc_filecache`.`fileid` = ".$row['fileid']);
  302. }
  303. else {
  304. echo " ONLY with \$TEST = 0 the DB entry will be removed!";
  305. }
  306. echo "\n";
  307. }
  308. }
  309. }
  310. $result->free_result();
  311. }
  312. if ($PREVIEW_DEL[0] > 0
  313. || $PREVIEW_REM[0] > 0) {
  314. echo "\nappdata preview size before :".sprintf('% 8.2f',($PREVIEW_NOW[1]+$PREVIEW_DEL[1])/1024/1024)." Mb\t(".($PREVIEW_NOW[0]+$PREVIEW_DEL[0])." files)";
  315. echo "\nappdata preview > 1 year old:".sprintf('% 8.2f',($PREVIEW_1YR[1])/1024/1024)." Mb\t(".$PREVIEW_1YR[0]." files)";
  316. echo "\nappdata preview size cleared:".sprintf('% 8.2f',($PREVIEW_DEL[1])/1024/1024)." Mb\t(".$PREVIEW_DEL[0]." files".($PREVIEW_MAX_DEL<1?' MAX DEL ':'').")";
  317. echo "\nappdata preview size cleared:".sprintf('% 8.2f',($PREVIEW_DEL[1])/1024/1024)." Mb\t(".$PREVIEW_DEL[0]." files".($PREVIEW_MAX_DEL<1?' MAX DEL ':'').")";
  318. echo "\nappdata preview size now :".sprintf('% 8.2f',($PREVIEW_NOW[1])/1024/1024)." Mb\t(".$PREVIEW_NOW[0]." files";
  319. if ($PREVIEW_NOW[1]+$PREVIEW_DEL[1] > 0 ) {
  320. echo "/ -".floor(($PREVIEW_DEL[1]+$PREVIEW_REM[1])/($PREVIEW_NOW[1]+$PREVIEW_DEL[1])+.5)."%";
  321. }
  322. echo ")";
  323. if (!empty($TEST)) {
  324. echo "\n\nNOTE: in TEST-mode, no preview-data has been cleared!";
  325. }
  326. } else {
  327. echo "\nappdata preview size :".sprintf('% 8.2f',($PREVIEW_NOW[1])/1024/1024)." Mb\t(".$PREVIEW_NOW[0]." files)";
  328. echo "\nappdata preview > 1 year old:".sprintf('% 8.2f',($PREVIEW_1YR[1])/1024/1024)." Mb\t(".$PREVIEW_1YR[0]." files)";
  329. }
  330. echo "\n".
  331. "\n#########################################################################################".
  332. "\nread files in S3...";
  333. $objects = S3list($s3, $bucket);
  334. $objectIDs = array();
  335. $objectIDsSize = 0;
  336. $users = array();
  337. if (is_string($objects)) {
  338. echo $objects; # error..
  339. die;
  340. }
  341. else {
  342. echo "\nObjects to process in S3: ".count($objects).' ';
  343. $S3_removed = [0,0];
  344. $S3_updated = [0,0];
  345. $S3_skipped = [0,0];
  346. // Init progress
  347. $complete = count($objects);
  348. $prev = '';
  349. $current = 0;
  350. $showinfo = !empty($TEST);
  351. $showinfo = $SHOWINFO ? $showinfo : 0;
  352. foreach ($objects as $object) {
  353. $current++;
  354. $infoLine = "\n".$current." / ".substr($object['Key'],8)."\t".$object['Key'] . "\t" . $object['Size'] . "\t" . $object['LastModified'] . "\t";
  355. if (!$result = $mysqli->query("SELECT `ST`.`id`, `FC`.`fileid`, `FC`.`path`, `FC`.`storage_mtime`, `FC`.`size` FROM".
  356. " `oc_filecache` AS `FC`,".
  357. " `oc_storages` AS `ST`,".
  358. " `oc_mimetypes` AS `MT`".
  359. " WHERE 1".
  360. # " AND st.id LIKE 'object::%'".
  361. " AND `FC`.`fileid` = '".substr($object['Key'],8)."'". # should be only one..
  362. " AND `ST`.`numeric_id` = `FC`.`storage`".
  363. " AND `FC`.`mimetype` = `MT`.`id`".
  364. " AND `MT`.`mimetype` != 'httpd/unix-directory'".
  365. " ORDER BY `FC`.`path` ASC")) {#
  366. echo "\nERROR: query pos 2";
  367. die;
  368. } else {
  369. if ($result->num_rows>1) {
  370. echo "\ndouble file found in oc_filecache, this can not be!?\n";
  371. die;
  372. }
  373. else if ($result->num_rows == 0) { # in s3, not in db, remove from s3
  374. if ($showinfo) { echo $infoLine."\nID:".$object['Key']."\ton S3, but not in oc_filecache, remove..."; }
  375. if (!empty($TEST) && $TEST == 2) {
  376. echo ' not removed ($TEST = 2)';
  377. } else {
  378. $result_s3 = S3del($s3, $bucket, $object['Key']);
  379. if ($showinfo) { echo 'S3del:'.$result_s3; }
  380. }
  381. $S3_removed[0]++;
  382. $S3_removed[1]+=$object['Size'];
  383. }
  384. else { # one match, up to date?
  385. $row = $result->fetch_assoc();
  386. // Determine correct path
  387. if (substr($row['id'], 0, 13) == 'object::user:') {
  388. $path = $PATH_DATA . DIRECTORY_SEPARATOR . substr($row['id'], 13) . DIRECTORY_SEPARATOR . $row['path'];
  389. }
  390. else if (substr($row['id'], 0, 6) == 'home::') {
  391. $path = $PATH_DATA . DIRECTORY_SEPARATOR . substr($row['id'], 6) . DIRECTORY_SEPARATOR . $row['path'];
  392. } else {
  393. $path = $PATH_DATA . DIRECTORY_SEPARATOR . $row['path'];
  394. }
  395. $user = substr($path, strlen($PATH_DATA. DIRECTORY_SEPARATOR));
  396. $user = substr($user,0,strpos($user,DIRECTORY_SEPARATOR));
  397. $users[ $user ] = 1;
  398. $infoLine.= $user. "\t";
  399. # just for one user? set test = appdata_oczvcie795w3 (system wil not go to maintenance nor change database, just test and copy data!!)
  400. if (is_numeric($TEST) || $TEST == $user ) {
  401. $newpath = shell_exec('grep '.$user.' ./uid_login.txt | cut -d" " -f2');
  402. if (!empty($newpath)) {
  403. $user_directory = rtrim($newpath);
  404. $path = $user_directory . DIRECTORY_SEPARATOR . $row['path'];
  405. }
  406. if(file_exists($path) && is_file($path)){
  407. if ($row['storage_mtime'] < filemtime($path) ) {
  408. if ($showinfo) { echo $infoLine."\nID:".$object['Key']."\ton S3, but is older then local, upload..."; }
  409. if (!empty($TEST) && $TEST == 2) {
  410. echo ' not uploaded ($TEST = 2)';
  411. } else {
  412. $result_s3 = S3put($s3, $bucket,[
  413. 'Key' => 'urn:oid:'.$row['fileid'],
  414. #'Body'=> "Hello World!!",
  415. 'SourceFile' => $path,
  416. 'ACL' => 'private'//public-read'
  417. ]);
  418. if ($showinfo) { echo 'S3put:'.$result_s3; }
  419. }
  420. $S3_updated[0]++;
  421. $S3_updated[1]+=$row['size'];
  422. } else {
  423. $objectIDs[ $row['fileid'] ] = 1;
  424. $objectIDsSize+=$row['size'];
  425. # if ($showinfo) { echo $infoLine."OK (".$row['fileid']." / ".(count($objectIDs)).")"; }
  426. }
  427. } else {
  428. $objectIDs[ $row['fileid'] ] = 1;
  429. $objectIDsSize+=$row['size'];
  430. # if ($showinfo) { echo $infoLine."OK-S3 (".$row['fileid']." / ".(count($objectIDs)).")"; }
  431. }
  432. } else {
  433. $S3_skipped[0]++;
  434. $S3_skipped[1]+=$row['size'];
  435. # if ($showinfo) { echo "SKIP (TEST=$TEST)"; }
  436. }
  437. }
  438. // Update progress
  439. $new = sprintf('%.2f',$current/$complete*100).'% (now at user '.$user.')';
  440. if ($prev != $new && !$showinfo) {
  441. echo str_repeat(chr(8) , strlen($prev) );
  442. $new.= (strlen($prev)<=strlen($new))? '' : str_repeat(' ' , strlen($prev)-strlen($new) );
  443. $prev = $new;
  444. echo $prev;
  445. }
  446. }
  447. $result->free_result();
  448. }
  449. if (!$showinfo) {
  450. echo str_repeat(chr(8) , strlen($prev) );
  451. $new = ' DONE ';
  452. $new.= (strlen($prev)<=strlen($new))? '' : str_repeat(' ' , strlen($prev)-strlen($new) );
  453. $prev = $new;
  454. echo $prev;
  455. }
  456. if ($showinfo) { echo "\nNumber of objects in S3: ".count($objects); }
  457. echo "\nobjects removed from S3: ".$S3_removed[0] ."\t(".readableBytes($S3_removed[1]).")";
  458. echo "\nobjects updated to S3: ".$S3_updated[0] ."\t(".readableBytes($S3_updated[1]).")";
  459. echo "\nobjects skipped on S3: ".$S3_skipped[0] ."\t(".readableBytes($S3_skipped[1]).")";
  460. echo "\nobjects in sync on S3: ".count($objectIDs)."\t(".readableBytes($objectIDsSize).")";
  461. if ($S3_removed[0]+$S3_updated[0]+$S3_skipped[0]+count($objectIDs) - count($objects) != 0 ) {
  462. echo "\n\nERROR: The numbers do not add up!?\n\n";
  463. die;
  464. }
  465. }
  466. echo "\n".
  467. "\n#########################################################################################".
  468. "\ncheck files in oc_filecache... ";
  469. if (!$result = $mysqli->query("SELECT `ST`.`id`, `FC`.`fileid`, `FC`.`path`, `FC`.`storage_mtime`, `FC`.`size` FROM".
  470. " `oc_filecache` AS `FC`,".
  471. " `oc_storages` AS `ST`,".
  472. " `oc_mimetypes` AS `MT`".
  473. " WHERE 1".
  474. # " AND fc.size != 0".
  475. # " AND st.id LIKE 'object::%'".
  476. # " AND fc.fileid = '".substr($object['Key'],8)."'". # should be only one..
  477. " AND `ST`.`numeric_id` = `FC`.`storage`".
  478. " AND `FC`.`mimetype` = `MT`.`id`".
  479. " AND `MT`.`mimetype` != 'httpd/unix-directory'".
  480. " ORDER BY `ST`.`id`, `FC`.`fileid` ASC")) {
  481. echo "\nERROR: query pos 3\n\n";
  482. die;
  483. } else {
  484. // Init progress
  485. $complete = $result->num_rows;
  486. $prev = '';
  487. $current = 0;
  488. echo "\nNumber of objects in oc_filecache: ".$result->num_rows.' ';
  489. $showinfo = !empty($TEST);
  490. $showinfo = 0;
  491. $LOCAL_ADDED = [0,0];
  492. while ($row = $result->fetch_assoc()) {
  493. $current++;
  494. if (empty($objectIDs[ $row['fileid'] ]) ) {
  495. // Determine correct path
  496. if (substr($row['id'], 0, 13) == 'object::user:') {
  497. $path = $PATH_DATA . DIRECTORY_SEPARATOR . substr($row['id'], 13) . DIRECTORY_SEPARATOR . $row['path'];
  498. }
  499. else if (substr($row['id'], 0, 6) == 'home::') {
  500. $path = $PATH_DATA . DIRECTORY_SEPARATOR . substr($row['id'], 6) . DIRECTORY_SEPARATOR . $row['path'];
  501. } else {
  502. $path = $PATH_DATA . DIRECTORY_SEPARATOR . $row['path'];
  503. }
  504. $user = substr($path, strlen($PATH_DATA. DIRECTORY_SEPARATOR));
  505. $user = substr($user,0,strpos($user,DIRECTORY_SEPARATOR));
  506. $users[ $user ] = 1;
  507. if ($showinfo) { echo "\n".$user."\t".$row['fileid']."\t".$path."\t"; }
  508. # just for one user? set test = appdata_oczvcie795w3 (system wil not go to maintenance nor change database, just test and copy data!!)
  509. if (is_numeric($TEST) || $TEST == $user ) {
  510. $newpath = shell_exec('grep '.$user.' ./uid_login.txt | cut -d" " -f2');
  511. if (!empty($newpath)) {
  512. $user_directory = rtrim($newpath);
  513. $path = $user_directory . DIRECTORY_SEPARATOR . $row['path'];
  514. }
  515. echo "\n".$user."\t".$row['fileid']."\t".$path."\t";
  516. if(file_exists($path) && is_file($path)){
  517. if (!empty($TEST) && $TEST == 2) {
  518. echo ' not uploaded ($TEST = 2)';
  519. } else {
  520. $result_s3 = S3put($s3, $bucket,[
  521. 'Key' => 'urn:oid:'.$row['fileid'],
  522. #'Body'=> "Hello World!!",
  523. 'SourceFile' => $path,
  524. 'ACL' => 'private'//public-read'
  525. ]);
  526. if (strpos(' '.$result_s3,'ERROR:') == 1) {
  527. echo "\n".$result_s3."\n\n";
  528. die;
  529. }
  530. if ($showinfo) { echo "OK"; }
  531. }
  532. $LOCAL_ADDED[0]++;
  533. $LOCAL_ADDED[1]+=$row['size'];
  534. } else {
  535. echo "\n".$path." (id:".$row['fileid'].") DOES NOT EXIST?!\n";
  536. if (empty($TEST)) {
  537. $mysqli->query("DELETE FROM `oc_filecache` WHERE `oc_filecache`.`fileid` = ".$row['fileid']);
  538. echo "\t".'removed ($TEST = 0)'."\n";
  539. } else {
  540. echo "\t".'not removed ($TEST != 0)'."\n";
  541. }
  542. }
  543. } else if ($showinfo) {
  544. echo "SKIP (\$TEST = $TEST)";
  545. }
  546. } else {
  547. if ($showinfo) { echo "\n"."\t".$row['fileid']."\t".$row['path']."\t"."SKIP";}
  548. }
  549. // Update progress
  550. $new = sprintf('%.2f',$current/$complete*100).'% (now at user '.$user.')';
  551. if ($prev != $new && !$showinfo) {
  552. echo str_repeat(chr(8) , strlen($prev) );
  553. $new.= (strlen($prev)<=strlen($new))? '' : str_repeat(' ' , strlen($prev)-strlen($new) );
  554. $prev = $new;
  555. echo $prev;
  556. }
  557. }
  558. $result->free_result();
  559. if (!$showinfo) {
  560. echo str_repeat(chr(8) , strlen($prev) );
  561. $new = ' DONE ';
  562. $new.= (strlen($prev)<=strlen($new))? '' : str_repeat(' ' , strlen($prev)-strlen($new) );
  563. $prev = $new;
  564. echo $prev;
  565. }
  566. echo "\nFiles in oc_filecache added to S3: ".$LOCAL_ADDED[0]."\t(".readableBytes($LOCAL_ADDED[1]).")";
  567. }
  568. echo "\nCopying files finished";
  569. echo "\n". # inspiration source: https://github.com/otherguy/nextcloud-cleanup/blob/main/clean.php
  570. "\n#########################################################################################".
  571. "\ncheck for canceled uploads in oc_filecache...".
  572. "\n=> EXPERIMENTAL, I have not had this problem, so can not test.. => check only!";
  573. if (!$result = $mysqli->query("SELECT `oc_filecache`.`fileid`, `oc_filecache`.`path`, `oc_filecache`.`parent`, `oc_storages`.`id` AS `storage`, `oc_filecache`.`size`".
  574. " FROM `oc_filecache`".
  575. " LEFT JOIN `oc_storages` ON `oc_storages`.`numeric_id` = `oc_filecache`.`storage`".
  576. " WHERE `oc_filecache`.`parent` IN (".
  577. " SELECT `fileid`".
  578. " FROM `oc_filecache`".
  579. " WHERE `parent` IN (SELECT fileid FROM `oc_filecache` WHERE `path`='uploads')".
  580. " AND `storage_mtime` < UNIX_TIMESTAMP(NOW() - 24 * 60 * 60)".
  581. " ) AND `oc_storages`.`available` = 1")) {
  582. echo "\nERROR: query pos 4";
  583. die;
  584. } else {
  585. $S3_removed = [0,0];
  586. $S3_PARENTS = [];
  587. while ($row = $result->fetch_assoc()) {
  588. echo "\nCanceled upload: ".$row['path']." ( ".$row['size']." bytes)";
  589. $S3_removed[0]++;
  590. $S3_removed[1]+=$row['size'];
  591. // Add parent object to array
  592. $S3_PARENTS[] = $row['parent'];
  593. if ( 1 ) {
  594. echo ' EXPERIMENTAL: no deletion, only detection';
  595. } else
  596. if (!empty($TEST) && $TEST == 2) {
  597. echo ' not removed ($TEST = 2)';
  598. } else {
  599. $result_s3 = S3del($s3, $bucket, 'urn:oid:'.$row['fileid']);
  600. if ($showinfo) { echo 'S3del:'.$result_s3; }
  601. $mysqli->query("DELETE FROM `oc_filecache` WHERE `oc_filecache`.`fileid` = ".$row['fileid']);
  602. }
  603. }
  604. if ($S3_removed[0] > 0 ) {
  605. echo "\nobjects removed from S3: ".$S3_removed[0]."\t(".readableBytes($S3_removed[1]).")";
  606. // Delete all parent objects from the db
  607. $S3_PARENTS = array_unique($S3_PARENTS);
  608. echo "\nremoving parents... (".count($S3_PARENTS)." database entries)";
  609. foreach ($S3_PARENTS as $s3_parent) {
  610. echo "\nparent obeject id: ".$s3_parent;
  611. if ( 1 ) {
  612. echo ' EXPERIMENTAL: no deletion, only detection';
  613. } else
  614. if (!empty($TEST) && $TEST == 2) {
  615. echo ' not removed ($TEST = 2)';
  616. } else {
  617. $mysqli->query("DELETE FROM `oc_filecache` WHERE `oc_filecache`.`fileid` = ".$s3_parent);
  618. echo ' removed';
  619. }
  620. }
  621. }
  622. }
  623. #########################################################################################
  624. if (empty($TEST)) {
  625. $dashLine = "\n".
  626. "\n#########################################################################################";
  627. $mysqli->query("UPDATE `oc_storages` SET `id`=CONCAT('object::user:', SUBSTRING_INDEX(`oc_storages`.`id`,':',-1)) WHERE `oc_storages`.`id` LIKE 'home::%'");
  628. $UpdatesDone = $mysqli->affected_rows;
  629. //rename command
  630. if ($LOCAL_STORE_ID == 0
  631. || $OBJECT_STORE_ID== 0) { // standard rename
  632. $mysqli->query("UPDATE `oc_storages` SET `id`='object::store:amazon::".$bucket."' WHERE `oc_storages`.`id` LIKE 'local::".$PATH_DATA."/'");
  633. $UpdatesDone.= '/'.$mysqli->affected_rows;
  634. } else {
  635. $mysqli->query("UPDATE `oc_filecache` SET `storage` = '".$OBJECT_STORE_ID."' WHERE `storage` = '".$LOCAL_STORE_ID."'");
  636. $UpdatesDone.= '/'.$mysqli->affected_rows;
  637. #$mysqli->query("DELETE FROM `oc_storages` WHERE `oc_storages`.`numeric_id` = ".$OBJECT_STORE_ID);
  638. }
  639. if ($UpdatesDone == '0/0' ) {
  640. # echo $dashLine." no modefications needed";
  641. } else {
  642. echo $dashLine."\noc_storages altered (".$UpdatesDone.")";
  643. }
  644. foreach ($users as $key => $value) {
  645. $mysqli->query("UPDATE `oc_mounts` SET `mount_provider_class` = REPLACE(`mount_provider_class`, 'LocalHomeMountProvider', 'ObjectHomeMountProvider') WHERE `user_id` = '".$key."'");
  646. if ($mysqli->affected_rows == 1) {
  647. echo $dashLine."\n-Changed mount provider class off ".$key." from home to object";
  648. $dashLine = '';
  649. }
  650. }
  651. echo "\n".
  652. "\n#########################################################################################";
  653. if ($PREVIEW_DEL[1] > 0 ) {
  654. echo "\nThere were preview images removed";
  655. echo "\nNOTE: you can optionally run occ preview:generate-all => pre generate previews, do install preview generator)\n";
  656. }
  657. foreach ($users as $key => $value) {
  658. if (is_dir($PATH_DATA . DIRECTORY_SEPARATOR . $key)) {
  659. echo "\nNOTE: you can remove the user folder of $key\tby: rm -rf ".$PATH_DATA . DIRECTORY_SEPARATOR . $key;
  660. }
  661. }
  662. echo "\n";
  663. if (is_string($CONFIG_OBJECTSTORE) && file_exists($CONFIG_OBJECTSTORE) ) {
  664. echo "\nCopy storage.config.php to the config folder...".
  665. copy($CONFIG_OBJECTSTORE,$PATH_NEXTCLOUD.'/config/storage.config.php');
  666. if ($SET_MAINTENANCE) { // maintenance mode
  667. $process = occ($OCC_BASE,'maintenance:mode --off');
  668. echo $process;
  669. }
  670. echo "\n#########################################################################################".
  671. "\n".
  672. "\nALL DONE!".
  673. "\n".
  674. "\nLog into your Nextcloud instance and check!".
  675. "\n".
  676. "\nIf all looks well: do not forget to remove '/config/storage.config.php' (it should be".
  677. "\n included in your config: having double config data is a risk..)".
  678. "\nIf it's not OK : set your instance in 'maintenance:mode --on' & restore your SQL backup".
  679. "\n you'll be back to 'local' (let me know, via GitHub, I'll try to help)".
  680. "\n#########################################################################################";
  681. }
  682. else if ($OBJECT_STORE_ID > 0 ) {
  683. if ($SET_MAINTENANCE) { // maintenance mode
  684. $process = occ($OCC_BASE,'maintenance:mode --off');
  685. echo $process;
  686. }
  687. echo "\n#########################################################################################".
  688. "\n".
  689. "\nALL DONE!".
  690. "\n".
  691. "\nLog into your Nextcloud instance and check!".
  692. "\n".
  693. "\n#########################################################################################";
  694. } else {
  695. echo "\n#########################################################################################".
  696. "\n".
  697. "\nALMOST done, one more step:".
  698. "\n".
  699. "\n ====== ! ! THIS MUST BE DONE MANUALY ! ! ======".
  700. "\n 1: add \$CONFIG_OBJECTSTORE to your config.php".
  701. "\n 2: turn maintenance mode off".
  702. "\n".
  703. "\nThe importance of the order to do this is EXTREME, other order can brick your Nextcloud!!\n".
  704. "\n".
  705. "\n#########################################################################################";
  706. }
  707. echo "\n\n";
  708. } else {
  709. echo "\n\ndone testing..\n";
  710. }
  711. #########################################################################################
  712. function occ($OCC_BASE,$OCC_COMMAND) {
  713. $result = "\nset ".$OCC_COMMAND.":\n";
  714. ob_start();
  715. passthru($OCC_BASE . $OCC_COMMAND);
  716. $process = ob_get_contents();
  717. ob_end_clean(); //Use this instead of ob_flush()
  718. return $result.$process."\n";
  719. }
  720. #########################################################################################
  721. function S3list($s3, $bucket, $maxIteration = 10000000) {
  722. $objects = [];
  723. try {
  724. $iteration = 0;
  725. $marker = '';
  726. do {
  727. $result = $s3->listObjects(['Bucket' => $bucket, 'Marker' => $marker]);
  728. if (rand(0,100) > 75 ) { echo '.'; }
  729. if ($result->get('Contents')) {
  730. $objects = array_merge($objects, $result->get('Contents'));
  731. }
  732. if (count($objects)) {
  733. $marker = $objects[count($objects) - 1]['Key'];
  734. }
  735. } while ($result->get('IsTruncated') && ++$iteration < $maxIteration);
  736. if ($result->get('IsTruncated')) {
  737. echo "\n".'WARNING: The number of keys greater than '.count($objects).' (the first part is loaded)';
  738. }
  739. return $objects;
  740. } catch (S3Exception $e) {
  741. return 'ERROR: Cannot retrieve objects: '.$e->getMessage();
  742. }
  743. }
  744. #########################################################################################
  745. function S3put($s3, $bucket, $vars = array() ) {
  746. #return 'dummy';
  747. if (is_string($vars) ) {
  748. if (file_exists($vars)) {
  749. $vars = array('SourceFile' => $vars);
  750. }
  751. else {
  752. return 'ERROR: S3put($cms, $bucket, $vars)';
  753. }
  754. }
  755. if (empty($vars['Bucket']) ) { $vars['Bucket'] = $bucket; }
  756. if (empty($vars['Key'])
  757. && !empty($vars['SourceFile'])) { $vars['Key'] = $vars['SourceFile']; }
  758. if (empty($vars['ACL']) ) { $vars['Key'] = 'private'; }
  759. if (empty($vars['Bucket']) ) { return 'ERROR: no Bucket'; }
  760. if (empty($vars['Key']) ) { return 'ERROR: no Key'; }
  761. if (!file_exists($vars['SourceFile'])) { return 'ERROR: file \''.$vars['SourceFile'].'\' does not exist'; }
  762. try {
  763. if (isset($GLOBALS['MULTIPART_THRESHOLD'])
  764. && filesize($vars['SourceFile']) > $GLOBALS['MULTIPART_THRESHOLD']*1024*1024) {
  765. /*
  766. $uploader = new MultipartUploader($s3,
  767. $vars['SourceFile'],
  768. $vars);
  769. $result = $uploader->upload();
  770. */
  771. // Using stream instead of file path
  772. $source = fopen($vars['SourceFile'], 'rb');
  773. $uploader = new ObjectUploader(
  774. $s3,
  775. $bucket,
  776. $vars['Key'],
  777. $source,
  778. $vars['ACL'],
  779. [ 'concurrency' => $GLOBALS['MULTIPART_CONCURRENCY'], 'part_size' => $GLOBALS['MULTIPART_THRESHOLD']*1024*1024 ]
  780. );
  781. do {
  782. try {
  783. $result = $uploader->upload();
  784. } catch (MultipartUploadException $e) {
  785. rewind($source);
  786. $uploader = new MultipartUploader($s3, $source, [
  787. 'state' => $e->getState(),
  788. 'acl' => $vars['ACL'],
  789. ]);
  790. }
  791. } while (!isset($result));
  792. fclose($source);
  793. } else {
  794. if (filesize($vars['SourceFile']) > 2*1024*1024*1024) {
  795. echo "\n".'WARNING: file \''.$vars['SourceFile'].'\' is larger then 2 Gb, consider enabling \'MultipartUploader\'';
  796. }
  797. $result = $s3->putObject($vars);
  798. }
  799. if (!empty($result['ObjectURL'])) {
  800. return 'OK: '.'ObjectURL:'.$result['ObjectURL'];
  801. } else {
  802. return 'ERROR: '.$vars['Key'].' was not uploaded';
  803. }
  804. } catch (MultipartUploadException | S3Exception | Exception $e) {
  805. return 'ERROR: ' . $e->getMessage();
  806. }
  807. }
  808. #########################################################################################
  809. function S3del($s3, $bucket, $vars = array() ) {
  810. #return 'dummy';
  811. if (is_string($vars) ) { $vars = array('Key' => $vars); }
  812. if (empty($vars['Bucket'])) { $vars['Bucket'] = $bucket; }
  813. if (empty($vars['Bucket'])) { return 'ERROR: no Bucket'; }
  814. if (empty($vars['Key']) ) { return 'ERROR: no Key'; }
  815. try {
  816. $result = $s3->deleteObject($vars);
  817. return 'OK: '.$vars['Key'].' was deleted (or didn\'t not exist)';
  818. } catch (S3Exception $e) { return 'ERROR: ' . $e->getMessage(); }
  819. }
  820. #########################################################################################
  821. function S3get($s3, $bucket, $vars = array() ) {
  822. #return 'dummy';
  823. if (is_string($vars) ) {
  824. $vars = array('Key' => $vars);
  825. }
  826. if (empty($vars['Bucket']) ) { $vars['Bucket'] = $bucket; } // Bucket = the bucket
  827. if (empty($vars['Key'])
  828. && !empty($vars['SaveAs'])) { $vars['Key'] = $vars['SaveAs']; } // Key = the file-id/location in s3
  829. if (empty($vars['SaveAs'])
  830. && !empty($vars['Key']) ) { $vars['SaveAs'] = $vars['Key']; } // SaveAs = local location+name
  831. if (empty($vars['Bucket'])) { return 'ERROR: no Bucket'; }
  832. if (empty($vars['Key']) ) { return 'ERROR: no Key'; }
  833. try {
  834. if (1 || $cms['aws']['client']->doesObjectExist($vars['Bucket']
  835. ,$vars['Key']) ) {
  836. return $cms['aws']['client']->getObject($vars);
  837. } else {
  838. return 'ERROR: '.$vars['Key'].' does not exist';
  839. }
  840. } catch (S3Exception $e) { return 'ERROR: ' . $e->getMessage(); }
  841. }
  842. #########################################################################################
  843. function readableBytes($bytes) {
  844. if ($bytes == 0) { return "0 bytes"; }
  845. $i = floor(log($bytes) / log(1024));
  846. $sizes = array('bytes', 'kb', 'Mb', 'Gb', 'Tb', 'Pb', 'Eb', 'Zb', 'Yb');
  847. return sprintf('% 5.2f', $bytes / pow(1024, $i)) * 1 . ' ' . $sizes[$i];
  848. #return sprintf('%.02F', $bytes / pow(1024, $i)) * 1 . ' ' . $sizes[$i];
  849. }