Current Path : /var/www/www-root/data/www/info.monolith-realty.ru/bitrix/modules/clouds/lib/ |
Current File : /var/www/www-root/data/www/info.monolith-realty.ru/bitrix/modules/clouds/lib/filehash.php |
<?php namespace Bitrix\Clouds; use Bitrix\Main\Localization\Loc; use Bitrix\Main\ORM\Data\DataManager; use Bitrix\Main\ORM\Fields\DatetimeField; use Bitrix\Main\ORM\Fields\IntegerField; use Bitrix\Main\ORM\Fields\StringField; use Bitrix\Main\ORM\Fields\Validators\LengthValidator; use Bitrix\Main\ORM\Fields\ExpressionField; Loc::loadMessages(__FILE__); /** * <ul> * <li> ID integer mandatory * <li> BUCKET_ID integer mandatory * <li> FILE_PATH string(600) mandatory * <li> FILE_SIZE int optional * <li> FILE_MTIME datetime optional * <li> FILE_HASH string(50) optional * </ul> * * @package Bitrix\Clouds * * DO NOT WRITE ANYTHING BELOW THIS * * <<< ORMENTITYANNOTATION * @method static EO_FileHash_Query query() * @method static EO_FileHash_Result getByPrimary($primary, array $parameters = array()) * @method static EO_FileHash_Result getById($id) * @method static EO_FileHash_Result getList(array $parameters = array()) * @method static EO_FileHash_Entity getEntity() * @method static \Bitrix\Clouds\EO_FileHash createObject($setDefaultValues = true) * @method static \Bitrix\Clouds\EO_FileHash_Collection createCollection() * @method static \Bitrix\Clouds\EO_FileHash wakeUpObject($row) * @method static \Bitrix\Clouds\EO_FileHash_Collection wakeUpCollection($rows) */ class FileHashTable extends DataManager { /** * Returns DB table name for entity. * * @return string */ public static function getTableName() { return 'b_clouds_file_hash'; } /** * Returns entity map definition. * * @return array */ public static function getMap() { return [ new IntegerField( 'ID', [ 'primary' => true, 'autocomplete' => true, 'title' => Loc::getMessage('FILE_HASH_ENTITY_ID_FIELD'), ] ), new IntegerField( 'BUCKET_ID', [ 'required' => true, 'title' => Loc::getMessage('FILE_HASH_ENTITY_BUCKET_ID_FIELD'), ] ), new StringField( 'FILE_PATH', [ 'required' => true, 'validation' => [__CLASS__, 'validateFilePath'], 'title' => Loc::getMessage('FILE_HASH_ENTITY_FILE_PATH_FIELD'), ] ), new IntegerField( 'FILE_SIZE', [ 'title' => Loc::getMessage('FILE_HASH_ENTITY_FILE_SIZE_FIELD'), ] ), new DatetimeField( 'FILE_MTIME', [ 'title' => Loc::getMessage('FILE_HASH_ENTITY_FILE_MTIME_FIELD'), ] ), new StringField( 'FILE_HASH', [ 'validation' => [__CLASS__, 'validateFileHash'], 'title' => Loc::getMessage('FILE_HASH_ENTITY_FILE_HASH_FIELD'), ] ), ]; } /** * Returns validators for FILE_PATH field. * * @return array */ public static function validateFilePath() { return [ new LengthValidator(null, 760), ]; } /** * Returns validators for FILE_HASH field. * * @return array */ public static function validateFileHash() { return [ new LengthValidator(null, 50), ]; } /** * Stores file hashes to the database. * * @param integer $bucketId Clouds storage bucket identifier. * @param array $files File list as it returned by \CCloudStorageBucket::ListFiles. * * @return \Bitrix\Main\DB\Result * @see \CCloudStorageBucket::ListFiles */ public static function addList($bucketId, array $files) { $bucketId = intval($bucketId); $connection = \Bitrix\Main\Application::getConnection(); $helper = $connection->getSqlHelper(); $values = []; foreach ($files['file'] as $i => $file) { $fileSize = $files['file_size'][$i]; $fileMtime = \CCloudUtil::gmtTimeToDateTime($files['file_mtime'][$i]); $fileHash = $files['file_hash'][$i]; $values [] = '(' . $bucketId . ",'" . $helper->forSql($file) . "'" . ',' . intval($fileSize) . ",'" . $fileMtime->format('Y-m-d h:i:s') . "'" . ",'" . $helper->forSql($fileHash) . "'" . ')' ; } $sql = ' INSERT INTO ' . static::getTableName() . ' (BUCKET_ID, FILE_PATH, FILE_SIZE, FILE_MTIME, FILE_HASH) VALUES ' . implode(",\n", $values) . ' '; return $connection->query($sql); } /** * Sync file hashes to the database. Adds new keys and removes missing between $files[last_key] and $lastKey. * * @param integer $bucketId Clouds storage bucket identifier. * @param string $path File list relative path. * @param array $files File list as it returned by CCloudStorageBucket::ListFiles. * @param string $prevLastKey Last key returned by previous call to CCloudStorageBucket::ListFiles. * * @return null|\Bitrix\Main\DB\Result * @see \CCloudStorageBucket::ListFiles */ public static function syncList($bucketId, $path, array $files, $prevLastKey) { $result = null; $bucketId = intval($bucketId); $connection = \Bitrix\Main\Application::getConnection(); $helper = $connection->getSqlHelper(); $index = []; foreach ($files['file'] as $i => $filePath) { $index[$path . $filePath] = $i; } $filter = [ '=BUCKET_ID' => $bucketId, ]; if ($prevLastKey) { $filter['>FILE_PATH'] = $path . $prevLastKey; } if ($files['last_key']) { $filter['<=FILE_PATH'] = $path . $files['last_key']; } $fileList = static::getList([ 'select' => ['ID', 'FILE_PATH', 'FILE_SIZE', 'FILE_HASH'], 'filter' => $filter, ]); while ($fileInfo = $fileList->fetch()) { if ( array_key_exists($fileInfo['FILE_PATH'], $index) && ($files['file_size'][$index[$fileInfo['FILE_PATH']]] == $fileInfo['FILE_SIZE']) && ($files['file_hash'][$index[$fileInfo['FILE_PATH']]] == $fileInfo['FILE_HASH']) ) { unset($files['file'][$index[$fileInfo['FILE_PATH']]]); } else { $deleteResult = static::delete($fileInfo['ID']); } } $values = []; foreach ($files['file'] as $i => $file) { $fileSize = $files['file_size'][$i]; $fileMtime = \CCloudUtil::gmtTimeToDateTime($files['file_mtime'][$i]); $fileHash = $files['file_hash'][$i]; $values [] = '(' . $bucketId . ",'" . $helper->forSql($path . $file) . "'" . ',' . intval($fileSize) . ",'" . $fileMtime->format('Y-m-d h:i:s') . "'" . ",'" . $helper->forSql($fileHash) . "'" . ')' ; } $insertSize = 1000; while ($values) { $sql = ' INSERT INTO ' . static::getTableName() . ' (BUCKET_ID, FILE_PATH, FILE_SIZE, FILE_MTIME, FILE_HASH) VALUES ' . implode(",\n", array_splice($values, 0, $insertSize)) . ' '; $result = $connection->query($sql); } return $result; } /** * Sync file hashes to the database. Removes all keys beyond the $prevLastKey. * * @param integer $bucketId Clouds storage bucket identifier. * @param string $path File list relative path. * @param string $prevLastKey Last key returned by last call to CCloudStorageBucket::ListFiles. * * @return null|\Bitrix\Main\DB\Result * @see \Bitrix\Clouds\FileHashTable::syncList */ public static function syncEnd($bucketId, $path, $prevLastKey) { $bucketId = intval($bucketId); $connection = \Bitrix\Main\Application::getConnection(); $sqlHelper = $connection->getSqlHelper(); $delete = ' DELETE from ' . static::getTableName() . ' WHERE BUCKET_ID = ' . $bucketId . ' AND FILE_PATH like \'' . $sqlHelper->forSql($path) . '%\' AND FILE_PATH > \'' . $sqlHelper->forSql($path . $prevLastKey) . '\' '; $result = $connection->query($delete); return $result; } /** * Add a file hash to the database. * * @param integer $bucketId Clouds storage bucket identifier. * @param string $path Path to the file. * @param array $fileInfo File info as it returned by CCloudStorageBucket::GetFileInfo. * * @return null|\Bitrix\Main\DB\Result * @see \CCloudStorageBucket::GetFileInfo */ public static function addFile($bucketId, $path, array $fileInfo) { return static::add([ 'BUCKET_ID' => $bucketId, 'FILE_PATH' => $path, 'FILE_SIZE' => $fileInfo['size'], 'FILE_MTIME' => \CCloudUtil::gmtTimeToDateTime($fileInfo['mtime']), 'FILE_HASH' => $fileInfo['hash'], ]); } /** * Returns last stored key for given bucket. * * @param integer $bucketId Clouds storage bucket identifier. * * @return string */ public static function getLastKey($bucketId) { $bucketId = intval($bucketId); $connection = \Bitrix\Main\Application::getConnection(); $sql = 'SELECT max(FILE_PATH) LAST_KEY from ' . static::getTableName() . ' WHERE BUCKET_ID=' . $bucketId; $last = $connection->query($sql)->fetch(); return $last && $last['LAST_KEY'] ? $last['LAST_KEY'] : ''; } /** * Clears all stored file hashes for the bucket. * * @param integer $bucketId Clouds storage bucket identifier. * * @return \Bitrix\Main\DB\Result */ public static function deleteAll($bucketId) { $bucketId = intval($bucketId); $connection = \Bitrix\Main\Application::getConnection(); $delete = 'DELETE from ' . static::getTableName() . ' WHERE BUCKET_ID=' . $bucketId; $result = $connection->query($delete); return $result; } /** * Clears a stored file hashe for the filePath for the bucket. * * @param integer $bucketId Clouds storage bucket identifier. * @param string $filePath File path. * * @return \Bitrix\Main\DB\Result */ public static function deleteByFilePath($bucketId, $filePath) { $bucketId = intval($bucketId); $connection = \Bitrix\Main\Application::getConnection(); $sqlHelper = $connection->getSqlHelper(); $delete = ' DELETE from ' . static::getTableName() . ' WHERE BUCKET_ID = ' . $bucketId . " AND FILE_PATH = '" . $sqlHelper->forSql($filePath) . "' "; $result = $connection->query($delete); return $result; } /** * Returns file listing with "folders", sizes and modification times. * * @param integer $bucketId Clouds storage bucket identifier. * @param string $path Directory path. * @param array $order How to sort. * @param array $filter Additional filter. * * @return \Bitrix\Main\DB\Result */ public static function dirList($bucketId, $path, $order, $filter) { $connection = \Bitrix\Main\Application::getConnection(); $sqlHelper = $connection->getSqlHelper(); $query = \Bitrix\Clouds\FileHashTable::query(); $query->setSelect([ new ExpressionField( 'FILE_TYPE', 'if(locate(\'/\', substring(%s, length(\'' . $sqlHelper->forSql($path) . '\')+1)) > 0, \'D\', \'F\')', ['FILE_PATH'] ), new ExpressionField( 'NAME', 'substring_index(substring(%s, length(\'' . $sqlHelper->forSql($path) . '\')+1), \'/\', 1)', ['FILE_PATH'] ), new ExpressionField( 'SUM_FILE_SIZE', 'SUM(%s)', ['FILE_SIZE'] ), new ExpressionField( 'MAX_FILE_MTIME', 'MAX(%s)', ['FILE_MTIME'] ), new ExpressionField( 'FILE_COUNT', 'COUNT(1)' ), ]); $filter['=BUCKET_ID'] = $bucketId; $filter['%=FILE_PATH'] = $path . '%'; $query->setFilter($filter); $query->setGroup(['FILE_TYPE', 'NAME']); $query->setOrder($order); $sql = $query->getQuery(); return $connection->query($sql); } /** * Returns duplicate files by bucket, hash, and size. * * @param integer $bucketId Clouds storage bucket identifier. * @param array $filter Additional filter to pass to FileHashTable. * @param array $order Sort order. * @param integer $limit Records count. * @return \Bitrix\Main\DB\Result * @see \Bitrix\Main\File\Internal\FileHashTable */ public static function duplicateList($bucketId, $filter, $order, $limit = 0) { $connection = \Bitrix\Main\Application::getConnection(); $query = \Bitrix\Main\File\Internal\FileHashTable::query(); $query->setSelect([ 'FILE_HASH', 'FILE_SIZE', new ExpressionField( 'FILE_COUNT', 'COUNT(distinct %s, %s)', ['FILE.SUBDIR', 'FILE.FILE_NAME'] ), new ExpressionField( 'FILE_ID_MIN', 'MIN(%s)', ['FILE_ID'] ), new ExpressionField( 'FILE_ID_MAX', 'MAX(%s)', ['FILE_ID'] ), ]); $filter['=FILE.HANDLER_ID'] = $bucketId; $filter['>FILE_COUNT'] = 1; $query->setFilter($filter); $query->setGroup(['FILE_HASH', 'FILE_SIZE']); $query->setOrder($order); if ($limit > 0) { $query->setLimit($limit); } $sql = $query->getQuery(); return $connection->query($sql); } /** * Returns duplicate files list by bucket, hash, and size. * * @param integer $bucketId Clouds storage bucket identifier. * @param array $fileHash File hash. * @param array $fileSize File size. * @return array * @see \Bitrix\Main\File\Internal\FileHashTable */ public static function getFileDuplicates($bucketId, $fileHash, $fileSize) { $query = \Bitrix\Main\File\Internal\FileHashTable::getList([ 'select' => [ 'FILE_ID', ], 'filter' => [ '=FILE.HANDLER_ID' => $bucketId, '=FILE_HASH' => $fileHash, '=FILE_SIZE' => $fileSize, ], 'order' => [ 'FILE_ID' => 'ASC', ], ]); $result = []; while ($fileDuplicate = $query->fetch()) { $result[] = $fileDuplicate['FILE_ID']; } return $result; } /** * Returns duplicates summary statistics. * * @param integer $bucketId Clouds storage bucket identifier. * @return array */ public static function getDuplicatesStat($bucketId) { $bucketId = intval($bucketId); $connection = \Bitrix\Main\Application::getConnection(); $sql = " select sum(DUP_COUNT) DUP_COUNT, sum(DUP_SIZE) DUP_SIZE from ( select b_file_hash.FILE_SIZE ,b_file_hash.FILE_HASH ,count(distinct b_file.SUBDIR, b_file.FILE_NAME)-1 DUP_COUNT ,(count(distinct b_file.SUBDIR, b_file.FILE_NAME)-1) * b_file_hash.FILE_SIZE DUP_SIZE from b_file_hash inner join b_file on b_file.ID = b_file_hash.FILE_ID where b_file.HANDLER_ID = " . $bucketId . " group by b_file_hash.FILE_SIZE, b_file_hash.FILE_HASH having count(distinct b_file.SUBDIR, b_file.FILE_NAME) > 1 ) t "; return $connection->query($sql)->fetch(); } /** * Copies file hash information from clouds module table to the main module. * Returns an array with copy statistics information. * * @param integer $lastKey Where to start. * @param integer $pageSize Batch size. * @return array */ public static function copyToFileHash($lastKey, $pageSize) { $lastKey = (int)$lastKey; $pageSize = (int)$pageSize; $sql = " SELECT b_file.ID as FILE_ID ,b_clouds_file_hash.FILE_SIZE as FILE_SIZE ,b_clouds_file_hash.FILE_HASH as FILE_HASH FROM b_file INNER JOIN b_clouds_file_hash ON b_clouds_file_hash.BUCKET_ID = b_file.HANDLER_ID AND b_clouds_file_hash.FILE_PATH = concat('/', b_file.SUBDIR, '/', b_file.FILE_NAME) LEFT JOIN b_file_duplicate ON b_file_duplicate.DUPLICATE_ID = b_file.ID WHERE b_file.ID > " . $lastKey . " AND b_file_duplicate.DUPLICATE_ID is null ORDER BY b_file.ID LIMIT " . $pageSize . " "; $connection = \Bitrix\Main\Application::getConnection(); $fileIds = $connection->query(' SELECT min(FILE_ID) as FILE_ID_MIN ,max(FILE_ID) as FILE_ID_MAX ,count(FILE_ID) FILE_ID_CNT FROM (' . $sql . ') t ')->fetch(); if ($fileIds['FILE_ID_CNT'] > 0) { $connection->queryExecute('INSERT IGNORE INTO b_file_hash '.$sql); } return $fileIds; } /** * Checks candidates from file duplicates delete. Returns future file original identifier. * * @param integer $bucketId Clouds storage bucket identifier. * @param array &$fileIds Array of file identifiers candidates for duplicate delete. * @return false|integer */ public static function prepareDuplicates($bucketId, &$fileIds) { $originalId = false; if ($fileIds) { //Exclude any id that is already a duplicate $duplicates = \Bitrix\Main\File\Internal\FileDuplicateTable::getList([ 'select' => ['DUPLICATE_ID'], 'filter' => [ '=DUPLICATE_ID' => $fileIds, ], ]); while ($duplicate = $duplicates->fetch()) { //Others will be excluded from the process $p = array_search($duplicate['DUPLICATE_ID'], $fileIds); if ($p !== false) { unset($fileIds[$p]); } } } if ($fileIds) { //Search throught file id for any existing originals $originals = \Bitrix\Main\File\Internal\FileDuplicateTable::getList([ 'select' => ['ORIGINAL_ID'], 'filter' => [ '=ORIGINAL_ID' => $fileIds, ], 'order' => ['ORIGINAL_ID' => 'ASC'], ]); while ($original = $originals->fetch()) { //First will be used for future deduplication if ($originalId === false) { $originalId = $original['ORIGINAL_ID']; } //Others will be excluded from the process $p = array_search($original['ORIGINAL_ID'], $fileIds); if ($p !== false) { unset($fileIds[$p]); } } //None originals exists yet if ($originalId === false) { $originalId = array_shift($fileIds); } } return $originalId; } }