* @link http://www.netcommons.org NetCommons Project * @license http://www.netcommons.org/license.txt NetCommons License */ App::uses('Component', 'Controller'); /** * Class DownloadComponent */ class DownloadComponent extends Component { /** * @var Controller コントローラ */ protected $_controller = null; /** * Called before the Controller::beforeFilter(). * * @param Controller $controller Instantiating controller * @return void */ public function initialize(Controller $controller) { $this->_controller = $controller; } /** * ダウンロード実行 * * @param int $contentId コンテンツID * @param array $options オプション field : ダウンロードのフィールド名, size: nullならオリジナル thumb, small, medium, big * @return CakeResponse|null * @throws ForbiddenException */ public function doDownload($contentId, $options = array()) { if (isset($options['field'])) { $fieldName = $options['field']; unset($options['field']); } else { if (isset($this->_controller->request->params['field_name'])) { $fieldName = $this->_controller->request->params['field_name']; } elseif (isset($this->_controller->params['pass'][0])) { $fieldName = $this->_controller->params['pass'][0]; } else { $fieldName = null; } } if (isset($options['size'])) { $size = $options['size']; unset($options['size']); } else { if (isset($this->_controller->request->params['size'])) { $size = $this->_controller->request->params['size']; } elseif (isset($this->_controller->params['pass'][1])) { $size = $this->_controller->params['pass'][1]; } else { $size = null; } } // ファイル情報取得 plugin_keyとコンテンツID、フィールドの情報が必要 $UploadFile = ClassRegistry::init('Files.UploadFile'); $pluginKey = Inflector::underscore($this->_controller->plugin); $file = $UploadFile->getFile($pluginKey, $contentId, $fieldName); if (! $file) { //データがない=リンク切れ。リンク切れの場合、ログアウトしないようにするため、メッセージを追加 throw new ForbiddenException('Not found file'); } return $this->_downloadUploadFile($file, $size, $options); } /** * UploadFileのID指定でのダウンロード実行 * * @param int $uploadFileId UploadFile ID * @param array $options オプション field : ダウンロードのフィールド名, size: nullならオリジナル thumb, small, medium, big * @param string $pluginKey プラグインキー * @return CakeResponse|null * @throws ForbiddenException */ public function doDownloadByUploadFileId($uploadFileId, $options = [], $pluginKey = 'wysiwyg') { if (isset($options['size'])) { $size = $options['size']; unset($options['size']); } else { if (isset($this->_controller->request->params['size'])) { $size = $this->_controller->request->params['size']; } elseif (isset($this->_controller->params['pass'][2])) { $size = $this->_controller->params['pass'][2]; } else { $size = null; } } // ファイル情報取得 plugin_keyとコンテンツID、フィールドの情報が必要 $UploadFile = ClassRegistry::init('Files.UploadFile'); $file = $UploadFile->findById($uploadFileId); if (! $file || $file['UploadFile']['plugin_key'] !== $pluginKey) { //データがない=リンク切れ。リンク切れの場合、ログアウトしないようにするため、メッセージを追加 throw new ForbiddenException('Not found file'); } return $this->_downloadUploadFile($file, $size, $options); } /** * UploadFileのデータ指定でのダウンロード実行 * * @param array $file UploadFile data * @param array $options オプション field : ダウンロードのフィールド名, size: nullならオリジナル thumb, small, medium, big * @return CakeResponse|null * @throws ForbiddenException */ public function doDownloadByUploadFile($file, $options = []) { if (isset($options['size'])) { $size = $options['size']; unset($options['size']); } else { if (isset($this->_controller->request->params['size'])) { $size = $this->_controller->request->params['size']; } else { $size = null; } } return $this->_downloadUploadFile($file, $size, $options); } /** * ダウンロード処理 * * @param array $file UploadFile data * @param string $size サムネイル名 * @param array $options オプション * @return CakeResponse|null * @throws ForbiddenException * @throws BadRequestException * @throws NotFoundException * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) */ protected function _downloadUploadFile($file, $size, $options) { $UploadFile = ClassRegistry::init('Files.UploadFile'); // ルームチェック if ($file['UploadFile']['room_id']) { $roomId = Current::read('Room.id'); if ($file['UploadFile']['room_id'] != $roomId) { throw new ForbiddenException('Not found file'); } } if ($file['UploadFile']['block_key']) { // block_keyによるガード $Block = ClassRegistry::init('Blocks.Block'); $uploadFileBlock = $Block->find('first', [ 'recursive' => -1, 'fields' => ['id', 'key', 'public_type', 'publish_start', 'publish_end'], 'conditions' => [ 'key' => $file['UploadFile']['block_key'] ] ] ); // ブロック見えない & ブロック編集できないのは 403 if (!$uploadFileBlock || ($Block->isVisible($uploadFileBlock) === false && !Current::permission('block_editable'))) { throw new ForbiddenException('Not found file'); } } // size対応 $filename = $file['UploadFile']['real_file_name']; if ($size) { // $size = '../../'とかを排除するため! if (strpos($size, '..') !== false) { throw new BadRequestException(); } $filename = $size . '_' . $filename; } $filePath = $UploadFile->uploadBasePath . $file['UploadFile']['path'] . $file['UploadFile']['id'] . DS . $filename; if (!file_exists($filePath)) { return null; } try { $downloadFileName = $file['UploadFile']['original_name']; if (! empty($options['name'])) { $downloadFileName = $options['name']; } $content = 'attachment;'; $content .= 'filename*=UTF-8\'\'' . rawurlencode($downloadFileName); $this->_controller->response->header('Content-Disposition', $content); // name, downloadが入っているとCake側処理により文字化けが発生する unset($options['name']); unset($options['download']); $this->_controller->response->file( $filePath, $options ); } catch (NotFoundException $ex) { //データがない=リンク切れ。リンク切れの場合、ログアウトしないようにする CakeLog::error($ex); throw new NotFoundException('Not found file'); } catch (Exception $ex) { CakeLog::error($ex); throw $ex; } // Download カウントアップ $UploadFile->countUp($file); return $this->_controller->response; } /** * ファイルが存在するかのチェック * * @param array $modelRecord AttachmentBehaviorを利用してfindで取得してきたレコード * $data['UploadFile'][フィールド名]にUploadFile情報がはいってる * @param string $field Modelで指定しているattachmentのフィールド名 * @param string|null $size 画像等の別サイズ指定 * @return bool */ public function existsRealFileByModelRecord( array $modelRecord, string $field, string $size = null ) : bool { $UploadFile = ClassRegistry::init('Files.UploadFile'); $filePath = $UploadFile->uploadBasePath . $modelRecord['UploadFile'][$field]['path'] . $modelRecord['UploadFile'][$field]['id'] . DS; $filename = $modelRecord['UploadFile'][$field]['real_file_name']; if ($size !== null) { $filename = $size . '_' . $filename; } $filePath .= $filename; return file_exists($filePath); } }