/** * 通过signature获得文件内容到内存 * @param $signature * @throws * @return mix */ public function getFileContentBySignature($signature) { //下载文件的hook $data = array(); $data["signature"] = $signature; $data["file_name"] = "text.txt"; $data["mime_type"] = "text/html"; //对网页的处理分为2种逻辑,-1种是直接显示内容,1种是文件直接下载 $data["force_download"] = -1; $retData = apply_filters("file_download_url", $data); if ($retData !== $data && !empty($retData)) { //通过迷你存储存储的文件,通过代理方式直接请求文件内容 $content = apply_filters("file_content", $signature); } else { $filePath = MiniUtil::getPathBySplitStr($signature); $oldPath = $filePath . "/" . $signature; //data源处理对象 $dataObj = Yii::app()->data; if ($dataObj->exists($oldPath)) { //兼容1.4老数据 $filePath = $oldPath; } else { if ($dataObj->exists($filePath) === false) { throw new MFilesException(Yii::t('api', MConst::NOT_FOUND), MConst::HTTP_CODE_404); } } $content = $dataObj->get_contents($filePath); } return $content; }
/** * 清理全部的缓存,包括多余的file_meta * @param $limit */ public function cleanCache($limit) { //data源处理对象 $dataObj = Yii::app()->data; // 回收站插件: -1保留值 0正常 1删除 $this->handleCleanFileMeta($limit); // 清理ref_count等于0的文件 $versions = MiniVersion::getInstance()->getCleanFiles(100); foreach ($versions as $version) { $files = UserFile::model()->findAll('version_id=?', array($version['id'])); // 如果$file存在此version_id,不删除 if (!empty($files)) { for ($i = 0; $i < count($files); $i++) { MiniVersion::getInstance()->updateRefCount($version["id"]); } continue; } // 如果不存在的话,删除流文件,删除该条version记录 $signature = $version['file_signature']; $signaturePath = MiniUtil::getPathBySplitStr($signature); // 判断文件是否存在 if ($dataObj->exists($signaturePath) === false) { MiniVersion::getInstance()->deleteById($version["id"]); continue; } // 删除文件 $dataObj->delete($signaturePath); //删除空的文件夹 $parts = CUtils::getFoldersBySplitStr($signature); foreach ($parts as $part) { $dataObj->delete($part); } // 删除version记录 MiniVersion::getInstance()->deleteById($version["id"]); } MiniUtil::deleteDir(BASE . 'temp'); }
/** * 创建对象 */ public function create() { // 查询文件信息 $path = MiniUtil::getAbsolutePath($this->user_id, $this->path); $file = MiniFile::getInstance()->getByPath($path); if (empty($file)) { throw new MException(Yii::t('api', MConst::PATH_ERROR), MConst::HTTP_CODE_404); } $fileName = $file["file_name"]; $fileSize = $file["file_size"]; $versionId = $file["version_id"]; // 检查是否支持缩略图 $this->checkExistThumbnail($fileName, $fileSize); // 获取文件版本 $version = MFileVersions::queryFileVersionByID($versionId); if (count($version) == 0) { throw new MException(Yii::t('api', MConst::PATH_ERROR), MConst::HTTP_CODE_404); } // 获取文件存储路径 $isTmp = false; $signature = $_REQUEST["signature"]; if (empty($signature) || $signature === "undefined") { $signature = $version[0]["file_signature"]; } // 缩略图大小 $sizeInfo = self::$sizes[$this->size]; if ($sizeInfo === NULL) { $sizeStr = strtolower($this->size); $sizeList = explode("x", $sizeStr); $sizeInfo = array("w" => $sizeList[0], "h" => $sizeList[1]); } $this->width = $sizeInfo["w"]; $this->height = $sizeInfo["h"]; // 检查缩略图是否存在 $thumbnail = THUMBNAIL_TEMP . MiniUtil::getPathBySplitStr($signature); $thumbnail .= "_{$this->width}_{$this->height}.{$this->format}"; if (file_exists($thumbnail) == true) { //直接跳转,避免重复生成缩略图 $url = MiniHttp::getMiniHost() . "assets/thumbnails/" . MiniUtil::getPathBySplitStr($signature); $url .= "_{$this->width}_{$this->height}.{$this->format}"; header('Location: ' . $url); exit; } //判断文件是否在迷你存储中,兼容非迷你存储的文件 $version = MiniVersion::getInstance()->getBySignature($signature); $meta = MiniVersionMeta::getInstance()->getMeta($version["id"], "store_id"); $thumbnailData = array(); if (!empty($meta)) { //为迷你存储缩略图添加hook $thumbnailData["signature"] = $signature; $storePath = apply_filters("image_path", $thumbnailData); } if (empty($storePath) || $storePath === $thumbnailData) { //data源处理对象 $dataObj = Yii::app()->data; $signaturePath = MiniUtil::getPathBySplitStr($signature); if ($dataObj->isExistLocal()) { $storePath = $dataObj->documentStorePath($signaturePath) . $signaturePath; } } if (file_exists($storePath) == false) { throw new MException(Yii::t('api', "The file path was not found."), MConst::HTTP_CODE_404); } $pathInfo = MUtils::pathinfo_utf($fileName); $extension = $pathInfo["extension"]; $tmpPath = DOCUMENT_TEMP . $signature . ".{$extension}"; // 缩略图对象 $this->handler = NULL; $this->image = $tmpPath; $this->resize = true; // 创建缩略图片父目录 if (file_exists(dirname($thumbnail)) == false) { if (MUtils::MkDirsLocal(dirname($thumbnail)) == false) { throw new MException(Yii::t('api', "The file path was not found."), MConst::HTTP_CODE_404); } } // 临时文件父目录 if (file_exists(dirname($tmpPath)) == false) { if (MUtils::MkDirsLocal(dirname($tmpPath)) == false) { throw new MException(Yii::t('api', "The file path was not found."), MConst::HTTP_CODE_404); } } // 拷贝文件到临时目录 if (file_exists($tmpPath) == false) { if (copy($storePath, $tmpPath) == false) { throw new MException(Yii::t('api', "The file path was not found."), MConst::HTTP_CODE_404); } } // 如果图片格式与后缀不一致,转换为一致的 if ($this->format != strtolower($extension)) { $fm = new Image($tmpPath); $format_path = DOCUMENT_TEMP . $signature . ".{$this->format}"; $fm->save($format_path); // 转换成功删除临时文件 unlink($tmpPath); $this->image = $format_path; } if ($isTmp) { unlink($storePath); } // 初始化图像对象 try { $this->handler = new Image($this->image, isset($this->config) ? $this->config : NULL); } catch (MException $e) { Yii::log("Exception : {$e->getTraceAsString()}"); throw new MException(Yii::t('api', "The image is invalid and cannot be thumbnailed."), MConst::HTTP_CODE_415); } // 生成缩略图 if ($this->resize == true) { $this->handler->resize($this->width, $this->height)->rotate(0)->quality(75)->sharpen(20); $chmod = 0644; $keep_actions = true; try { $this->handler->save($thumbnail, $chmod, $keep_actions); $this->handler->setImageFile($thumbnail); $this->image = $thumbnail; @unlink($format_path); } catch (MException $e) { Yii::trace("Exception : {$e}", "miniyun.api"); throw new MException(Yii::t('api', "The image is invalid and cannot be thumbnailed."), MConst::HTTP_CODE_415); } } }
/** * * 检查文件data和 meta是否存在 * @param string $hash 文件sha1 * @param string $fileName 文件名 * @return bool|void */ protected function handleCheckFileVersion($hash, $fileName) { $version = MiniVersion::getInstance()->getBySignature($hash); if (!$this->isNewVersion) { //data源处理对象 if ($version == null) { return $this->handleAssign(); } // 检查文件是否存在 $dataObj = Yii::app()->data; $storePath = MiniUtil::getPathBySplitStr($hash); if ($dataObj->exists($storePath) == false) { return $this->handleAssign(); } $this->version_id = $version['id']; $this->size = $version['file_size']; return true; } else { //返回断点文件信息 $data = array(); if (empty($version)) { $miniStoreInfo = MiniUtil::getPluginMiniStoreData(); if (empty($miniStoreInfo)) { //普通文件上传 $data['success'] = false; $data['url'] = MiniHttp::getMiniHost() . "api.php"; $storePath = MiniUtil::getPathBySplitStr($hash); $filePath = BASE . "upload_block/cache/" . $storePath; if (file_exists($filePath)) { $data['offset'] = filesize($filePath); //如文件大小相同而且Hash值相同,说明流数据文件已经存在,直接生成元数据即可 $size = MiniHttp::getParam("size", ""); if ($data['offset'] == $size) { //生成version记录,为使用老逻辑代码,这里处理得很羞涩 //理想的逻辑是在这里直接返回相关结果 $mimeType = MiniUtil::getMimeType($fileName); $version = MiniVersion::getInstance()->create($hash, $size, $mimeType); $this->version_id = $version['id']; $this->size = $version['file_size']; return true; } } else { $data['offset'] = 0; } echo json_encode($data); exit; } else { //迷你存储与第3方存储秒传接口 apply_filters("file_sec", array("route" => "module/miniStore/report", "sign" => MiniHttp::getParam("sign", ""), "access_token" => MiniHttp::getParam("access_token", ""), "signature" => $hash, "size" => MiniHttp::getParam("size", ""), "path" => MiniHttp::getParam("path", ""))); } } else { //上传文件到其它目录下,支持秒传 $this->version_id = $version['id']; $this->size = $version['file_size']; return true; } } }
/** * * 大文件断点多线程上传入口,使用put/post方法,推荐使用put * * header: * If-Match - request header,表示文件hash值 * ETag - response header,表示文件hash值 * X-Block-Length - 表示文件每个分片大小,第一次由客户端上传,若服务器端存在则与服务器端保持一致 * X-Description - response header 文件描述信息:00000000000010 1表示这部分内容已经收到,不用再传 * X-Content-Length - 文件总大小 * */ public function invoke() { //data源处理对象 $dataObj = Yii::app()->data; parent::init(); // 获取header值 $if_match = @$_SERVER['HTTP_IF_MATCH']; $clength = isset($_SERVER['CONTENT_LENGTH']) ? $_SERVER['CONTENT_LENGTH'] : 0; $crange = @$_SERVER['HTTP_CONTENT_RANGE']; $total_size = @$_SERVER['HTTP_X_CONTENT_LENGTH']; $block_size = @$_SERVER['HTTP_X_BLOCK_LENGTH']; // // 检查空间 // $user = MUserManager::getInstance()->getCurrentUser(); $space = $user["space"]; $used_space = $user["usedSpace"]; $used_space += $total_size; if ($used_space > $space) { throw new MFilesException(Yii::t('api', "User is over storage quota."), MConst::HTTP_CODE_507); } // // 文件临时存储路径 // $this->cache = DOCUMENT_CACHE . MiniUtil::getPathBySplitStr($if_match); $des_path = DOCUMENT_CACHE . MiniUtil::getPathBySplitStr($if_match) . ".des"; $store_path = MiniUtil::getPathBySplitStr($if_match); // 表示文件已经上传ok if ($dataObj->exists($store_path)) { $this->cache = $store_path; return true; } $this->description = $this->hanleDescription($des_path, $this->cache, $total_size, $block_size, $if_match); // // 表示查询 // if ($clength == 0 && preg_match("/^bytes \\*\\/([0-9]+)/i", $crange, $match)) { if ($this->handleCheckCompleted() == true) { // 上传完毕检查hash值 $this->handleCheck($this->cache, $if_match, $store_path, $des_path); $this->cache = $store_path; return true; } $this->handleAssigns(); } // // 获取文件上传的位置 // if (!preg_match("/^bytes ([0-9]+)-([0-9]+)/i", $crange, $match)) { throw new MFilesException(Yii::t('api', MConst::PARAMS_ERROR), MConst::HTTP_CODE_400); } $start = $match[1]; $end = $match[2]; $index = $start / $block_size; // // 若对应的文件已经上传则返回 // if ($this->description['blocks'][$index] != '1') { $this->handleWriteBytes($this->cache, $start, $clength); $this->description['blocks'][$index] = "1"; file_put_contents($des_path, serialize($this->description)); } // // 如果没有全部上传完成,则要求客户端继续上传 // if ($this->handleCheckCompleted() == false) { $this->handleAssigns(); } // 上传完毕检查hash值 $this->handleCheck($this->cache, $if_match, $store_path, $des_path); $this->cache = $store_path; }
/** * 保存文件版本 * @param $tmpName * @param $signature * @param $size * @param bool $move * @throws MFilesException */ public function saveFile($tmpName, $signature, $size, $move = true) { //data源处理对象 $dataObj = Yii::app()->data; // // 文件内容保存路径 // $storePath = MiniUtil::getPathBySplitStr($signature); if ($dataObj->exists(dirname($storePath)) === false) { MUtils::MkDirs(dirname($storePath)); } $version = MiniVersion::getInstance()->getBySignature($signature); if ($version != null) { // // 文件版本id // $this->version_id = $version["id"]; $this->file_hash = $version["file_signature"]; if ($dataObj->exists($storePath) == false) { if ($dataObj->put($tmpName, $storePath, true) == false) { throw new MFilesException(Yii::t('api', MConst::INTERNAL_SERVER_ERROR), MConst::HTTP_CODE_500); } } if ($move === true) { unlink($tmpName); } return; } // 移动临时文件到保存路径中 if ($move === true) { if ($dataObj->put($tmpName, $storePath, true) == false) { throw new MFilesException(Yii::t('api', MConst::INTERNAL_SERVER_ERROR), MConst::HTTP_CODE_500); } } // // 创建version // $version = MiniVersion::getInstance()->create($signature, $size, $this->type); if ($version == null) { throw new MFilesException(Yii::t('api', MConst::INTERNAL_SERVER_ERROR), MConst::HTTP_CODE_500); } // // 文件版本id // $this->version_id = $version["id"]; $this->file_hash = $version["file_signature"]; }