|
@@ -0,0 +1,404 @@
|
|
|
+<?php
|
|
|
+/**
|
|
|
+ * @Name
|
|
|
+ * @Description
|
|
|
+ * @Author 刘学玺
|
|
|
+ * @Date 2023/12/27 19:10
|
|
|
+ */
|
|
|
+
|
|
|
+namespace App\Common;
|
|
|
+
|
|
|
+
|
|
|
+use Illuminate\Http\Request;
|
|
|
+
|
|
|
+class UploadCommon
|
|
|
+{
|
|
|
+ private $error = false;
|
|
|
+ private string $formName = 'file';
|
|
|
+
|
|
|
+
|
|
|
+//app.post('/upload', (req, res) => {
|
|
|
+// const rangeHeader = req . headers['content-range'];
|
|
|
+// const fileSize = parseInt(rangeHeader . split('/')[1]);
|
|
|
+// const [start, end] = rangeHeader.replace(/bytes=([0-9]+)-([0-9]+)/, '$1-$2').split('-'); // 在此处处理文件上传逻辑,将接收到的文件片段追加到最终文件中 // 例如,可以使用fs模块将片段写入磁盘上的文件
|
|
|
+// res.sendStatus(200);});
|
|
|
+
|
|
|
+ public function upload(Request $request)
|
|
|
+ {
|
|
|
+// $arrFileTypes = [
|
|
|
+// 'image' => ['title' => 'Image files', 'extensions' => $uploadSetting['file_types']['image']['extensions']],
|
|
|
+// 'video' => ['title' => 'Video files', 'extensions' => $uploadSetting['file_types']['video']['extensions']],
|
|
|
+// 'audio' => ['title' => 'Audio files', 'extensions' => $uploadSetting['file_types']['audio']['extensions']],
|
|
|
+// 'file' => ['title' => 'Custom files', 'extensions' => $uploadSetting['file_types']['file']['extensions']]
|
|
|
+// ];
|
|
|
+
|
|
|
+ // 第一步 检测文件
|
|
|
+ $fileImage = $request->file($this->formName);
|
|
|
+ if (!$fileImage->isValid()) {
|
|
|
+ $this->error = "非法文件!";
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 第二步 检测类型
|
|
|
+// if (empty($arrData["filetype"])) {
|
|
|
+// $arrData["filetype"] = "image";
|
|
|
+// }
|
|
|
+// $fileType = $arrData["filetype"];
|
|
|
+
|
|
|
+ $originalName = $request->input('name');
|
|
|
+ $fileExtension = strtolower(get_file_extension($originalName));
|
|
|
+ $allowedExtensions = explode(',', 'jpg,jpeg,png,bmp,webp');
|
|
|
+ if (!in_array($fileExtension, $allowedExtensions) || $fileExtension == 'php') {
|
|
|
+ $this->error = "非法文件类型!";
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 第三步 保存文件
|
|
|
+ $uploadDirName = 'upload'; // 上传根目录配置
|
|
|
+ $uploadSubDir = $request->input('dir', 'default'); // 上传目录类型
|
|
|
+ if (empty($uploadSubDir) || !file_exists(public_path($uploadDirName . DIRECTORY_SEPARATOR . $uploadSubDir))) {
|
|
|
+ $uploadSubDir = 'default';
|
|
|
+ }
|
|
|
+
|
|
|
+ $chunk = $request->input('chunk', 0);
|
|
|
+ $chunks = $request->input('chunks', 1);
|
|
|
+ $chunkNext = $chunk + 1;
|
|
|
+ $continuation = !(!$chunk && $chunkNext === $chunks);
|
|
|
+ if ($continuation) { // 续传
|
|
|
+ $fileName = md5($originalName) . '_' . $chunk . '.part';
|
|
|
+ $uploadPath = storage_path($uploadDirName); // 上传目录绝对路径
|
|
|
+ $filePath = $uploadSubDir;
|
|
|
+ } else { // 秒传
|
|
|
+ $fileName = md5(uniqid()) . '.' . $fileExtension;
|
|
|
+ $uploadPath = public_path($uploadDirName); // 上传目录绝对路径
|
|
|
+ $filePath = $uploadSubDir . DIRECTORY_SEPARATOR . date('Ymd');
|
|
|
+ }
|
|
|
+
|
|
|
+ $savePath = $uploadPath . DIRECTORY_SEPARATOR . $filePath;
|
|
|
+ $fileImage->move($savePath, $fileName);
|
|
|
+
|
|
|
+ // 合并图片
|
|
|
+ if ($continuation && $chunkNext === intval($chunks)) {
|
|
|
+ $fileName = md5(uniqid()) . '.' . $fileExtension;
|
|
|
+ $filePath = $uploadSubDir . DIRECTORY_SEPARATOR . date('Ymd');
|
|
|
+ $continuationDir = public_path($uploadDirName) . DIRECTORY_SEPARATOR . $filePath;
|
|
|
+ $continuationPath = $continuationDir . DIRECTORY_SEPARATOR . $fileName;
|
|
|
+ if (!file_exists($continuationDir)) {
|
|
|
+ mkdir($continuationDir, 0777, true);
|
|
|
+ }
|
|
|
+ // 合并临时文件
|
|
|
+ if (!$out = @fopen($continuationPath, "wb")) {
|
|
|
+ $this->error = "上传目录不可写";
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ if (flock($out, LOCK_EX)) {
|
|
|
+ for ($index = 0; $index < $chunks; $index++) {
|
|
|
+ $inPath = $savePath . DIRECTORY_SEPARATOR . md5($originalName) . '_' . $index . '.part';
|
|
|
+ if (!$in = @fopen($inPath, "rb")) {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ while ($buff = fread($in, 4096)) {
|
|
|
+ fwrite($out, $buff);
|
|
|
+ }
|
|
|
+ fclose($in);
|
|
|
+ unlink($inPath);
|
|
|
+ }
|
|
|
+ flock($out, LOCK_UN);
|
|
|
+ }
|
|
|
+ @fclose($out);
|
|
|
+ @rmdir($savePath);
|
|
|
+ $continuation = false; // 停止续传
|
|
|
+ $savePath = $continuationDir;
|
|
|
+ }
|
|
|
+
|
|
|
+ $responseData = [
|
|
|
+ "status" => $continuation ? 'uploading' : 'done',
|
|
|
+ "percent" => number_format($chunkNext / $chunks, 2),
|
|
|
+ "chunk" => $chunkNext
|
|
|
+ ];
|
|
|
+ // 完成续传
|
|
|
+ if (!$continuation) {
|
|
|
+ // 第四步 检测大小
|
|
|
+
|
|
|
+ // 第五步 检测重复图片
|
|
|
+ $fileMd5 = md5_file($savePath . DIRECTORY_SEPARATOR . $fileName);
|
|
|
+
|
|
|
+ // 第六步 保存数据
|
|
|
+
|
|
|
+
|
|
|
+ // 第七步 返回数据
|
|
|
+ $host = $request->getSchemeAndHttpHost();
|
|
|
+ $url = DIRECTORY_SEPARATOR . $uploadDirName . DIRECTORY_SEPARATOR . $filePath . DIRECTORY_SEPARATOR . $fileName;
|
|
|
+ // composer require intervention/image
|
|
|
+ // use Intervention\Image\Facades\Image;
|
|
|
+ // $imageHeight = Image::make($path)->height();
|
|
|
+ $responseData["response"] = [
|
|
|
+ 'filepath' => $filePath,
|
|
|
+ "name" => $fileName,
|
|
|
+ 'preview_url' => $host . $url,
|
|
|
+ 'url' => $url,
|
|
|
+ "file_md5" => $fileMd5
|
|
|
+ ];
|
|
|
+ }
|
|
|
+ return $responseData;
|
|
|
+
|
|
|
+
|
|
|
+// if (array_key_exists($arrData["filetype"], $arrFileTypes)) {
|
|
|
+// $extensions = $uploadSetting['file_types'][$arrData["filetype"]]['extensions'];
|
|
|
+ $fileTypeUploadMaxFileSize = 10240;
|
|
|
+// } else {
|
|
|
+// $this->error = '上传文件类型配置错误!';
|
|
|
+// return false;
|
|
|
+// }
|
|
|
+// if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') { // other CORS headers if any...
|
|
|
+// exit; // finish preflight CORS requests here
|
|
|
+// }
|
|
|
+
|
|
|
+ $cleanupTargetDir = false; // Remove old files
|
|
|
+ $maxFileAge = 5 * 3600; // Temp file age in seconds
|
|
|
+
|
|
|
+ $fileUploadMaxFileSize = 10485760; // 10M
|
|
|
+// $fileUploadMaxFileSize = empty($fileUploadMaxFileSize) ? 2097152 : $fileUploadMaxFileSize;//默认2M
|
|
|
+
|
|
|
+ $strWebPath = "";//"upload" . DS;
|
|
|
+ $strId = $request->all("id");
|
|
|
+ $strDate = date('Ymd');
|
|
|
+
|
|
|
+// $adminId = cmf_get_current_admin_id();
|
|
|
+// $userId = cmf_get_current_user_id();
|
|
|
+// $userId = empty($adminId) ? $userId : $adminId;
|
|
|
+
|
|
|
+ $targetDir = $_ENV['UPLOAD_TMP_PATH'] . DIRECTORY_SEPARATOR . 'upload' . DIRECTORY_SEPARATOR; // 断点续传 need
|
|
|
+// if (!file_exists($targetDir)) {
|
|
|
+// mkdir($targetDir, 0777, true);
|
|
|
+// }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 断点续传 need
|
|
|
+ */
|
|
|
+ $strFilePath = md5($originalName);
|
|
|
+ $chunk = $request->input("chunk", 0);// isset($_REQUEST["chunk"]) ? intval($_REQUEST["chunk"]) : 0;
|
|
|
+ $chunks = $request->input("chunks", 1);//isset($_REQUEST["chunks"]) ? intval($_REQUEST["chunks"]) : 1;
|
|
|
+
|
|
|
+ // Open temp file
|
|
|
+ if (!$out = @fopen($targetDir . "{$strFilePath}_{$chunk}.parttmp", "wb")) {
|
|
|
+ $this->error = "上传文件临时目录不可写" . $targetDir;
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ // Read binary input stream and append it to temp file
|
|
|
+ if (!$in = @fopen($fileImage->getRealPath(), "rb")) {
|
|
|
+ $this->error = "Failed to open input stream!";
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ while ($buff = fread($in, 4096)) {
|
|
|
+ fwrite($out, $buff);
|
|
|
+ }
|
|
|
+
|
|
|
+ @fclose($out);
|
|
|
+ @fclose($in);
|
|
|
+
|
|
|
+ rename($targetDir . "{$strFilePath}_{$chunk}.parttmp", $targetDir . "{$strFilePath}_{$chunk}.part");
|
|
|
+
|
|
|
+ $done = true;
|
|
|
+ for ($index = 0; $index < $chunks; $index++) {
|
|
|
+ if (!file_exists($targetDir . "{$strFilePath}_{$index}.part")) {
|
|
|
+ $done = false;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!$done) {
|
|
|
+ /**
|
|
|
+ * 断点续传 need
|
|
|
+ */
|
|
|
+// header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
|
|
|
+// header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
|
|
|
+// header("Cache-Control: no-store, no-cache, must-revalidate");
|
|
|
+// header("Cache-Control: post-check=0, pre-check=0", false);
|
|
|
+// header("Pragma: no-cache");
|
|
|
+// header("Access-Control-Allow-Origin: *"); // Support CORS
|
|
|
+// die('');//分片没上传完
|
|
|
+// $response = Response::create();
|
|
|
+// throw new HttpResponseException($response);
|
|
|
+ }
|
|
|
+
|
|
|
+ $uploadPath = base_path() . 'upload/';
|
|
|
+
|
|
|
+ $fileSaveName = (empty($app) ? '' : $app . '/') . $strDate . '/' . md5(uniqid()) . "." . $strFileExtension;
|
|
|
+ $strSaveFilePath = $uploadPath . $fileSaveName; //TODO 测试 windows 下
|
|
|
+ $strSaveFileDir = dirname($strSaveFilePath);
|
|
|
+ if (!file_exists($strSaveFileDir)) {
|
|
|
+ mkdir($strSaveFileDir, 0777, true);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 合并临时文件
|
|
|
+ if (!$out = @fopen($strSaveFilePath, "wb")) {
|
|
|
+ $this->error = "上传目录不可写";
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (flock($out, LOCK_EX)) {
|
|
|
+ for ($index = 0; $index < $chunks; $index++) {
|
|
|
+ if (!$in = @fopen($targetDir . "{$strFilePath}_{$index}.part", "rb")) {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ while ($buff = fread($in, 4096)) {
|
|
|
+ fwrite($out, $buff);
|
|
|
+ }
|
|
|
+
|
|
|
+ fclose($in);
|
|
|
+ unlink("{$targetDir}{$strFilePath}_{$index}.part");
|
|
|
+ }
|
|
|
+ flock($out, LOCK_UN);
|
|
|
+ }
|
|
|
+ @fclose($out);
|
|
|
+
|
|
|
+ $fileImage = new File($strSaveFilePath, 'r');
|
|
|
+ $arrInfo = [
|
|
|
+ "name" => $originalName,
|
|
|
+ "type" => $fileImage->getMime(),
|
|
|
+ "tmp_name" => $strSaveFilePath,
|
|
|
+ "error" => 0,
|
|
|
+ "size" => $fileImage->getSize(),
|
|
|
+ ];
|
|
|
+
|
|
|
+ $fileImage->setSaveName($fileSaveName);
|
|
|
+ $fileImage->setUploadInfo($arrInfo);
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 断点续传 end
|
|
|
+ */
|
|
|
+
|
|
|
+ if (!$fileImage->validate(['size' => $fileUploadMaxFileSize])->check()) {
|
|
|
+ $error = $fileImage->getError();
|
|
|
+ unset($fileImage);
|
|
|
+ unlink($strSaveFilePath);
|
|
|
+ $this->error = $error;
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ // $url=$first['url'];
|
|
|
+ $storageSetting = cmf_get_cmf_settings('storage');
|
|
|
+ $qiniuSetting = $storageSetting['Qiniu']['setting'];
|
|
|
+ //$url=preg_replace('/^https/', $qiniu_setting['protocol'], $url);
|
|
|
+ //$url=preg_replace('/^http/', $qiniu_setting['protocol'], $url);
|
|
|
+
|
|
|
+ $arrInfo = [];
|
|
|
+ if (config('FILE_UPLOAD_TYPE') == 'Qiniu' && $qiniuSetting['enable_picture_protect']) {
|
|
|
+ //todo qiniu code ...
|
|
|
+ // $previewUrl = $url.$qiniuSetting['style_separator'].$qiniuSetting['styles']['thumbnail300x300'];
|
|
|
+ // $url= $url.$qiniuSetting['style_separator'].$qiniuSetting['styles']['watermark'];
|
|
|
+ } else {
|
|
|
+
|
|
|
+ if (empty($fileImage)) {
|
|
|
+ $this->error = $fileImage->getError();
|
|
|
+ return false;
|
|
|
+ } else {
|
|
|
+ $arrInfo["user_id"] = $userId;
|
|
|
+ $arrInfo["file_size"] = $fileImage->getSize();
|
|
|
+ $arrInfo["create_time"] = time();
|
|
|
+ $arrInfo["file_md5"] = md5_file($strSaveFilePath);
|
|
|
+ $arrInfo["file_sha1"] = sha1_file($strSaveFilePath);
|
|
|
+ $arrInfo["file_key"] = $arrInfo["file_md5"] . md5($arrInfo["file_sha1"]);
|
|
|
+ $arrInfo["filename"] = $fileImage->getInfo("name");
|
|
|
+ $arrInfo["file_path"] = $strWebPath . $fileSaveName;
|
|
|
+ $arrInfo["suffix"] = $fileImage->getExtension();
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ //关闭文件对象
|
|
|
+ $fileImage = null;
|
|
|
+ //检查文件是否已经存在
|
|
|
+ $assetModel = new AssetModel();
|
|
|
+ $objAsset = $assetModel->where(["user_id" => $userId, "file_key" => $arrInfo["file_key"]])->find();
|
|
|
+
|
|
|
+ $storage = cmf_get_option('storage');
|
|
|
+
|
|
|
+ if (empty($storage['type'])) {
|
|
|
+ $storage['type'] = 'Local';
|
|
|
+ }
|
|
|
+
|
|
|
+ $needUploadToRemoteStorage = false;//是否要上传到云存储
|
|
|
+ if ($objAsset && $storage['type'] == 'Local') {
|
|
|
+ $arrAsset = $objAsset->toArray();
|
|
|
+ //$arrInfo["url"] = $this->request->domain() . $arrAsset["file_path"];
|
|
|
+ $arrInfo["file_path"] = $arrAsset["file_path"];
|
|
|
+ if (file_exists($uploadPath . $arrInfo["file_path"])) {
|
|
|
+ @unlink($strSaveFilePath); // 删除已经上传的文件
|
|
|
+ } else {
|
|
|
+ $oldFileDir = dirname($uploadPath . $arrInfo["file_path"]);
|
|
|
+
|
|
|
+ if (!file_exists($oldFileDir)) {
|
|
|
+ mkdir($oldFileDir, 0777, true);
|
|
|
+ }
|
|
|
+
|
|
|
+ @rename($strSaveFilePath, $uploadPath . $arrInfo["file_path"]);
|
|
|
+ }
|
|
|
+
|
|
|
+ } else {
|
|
|
+ $needUploadToRemoteStorage = true;
|
|
|
+ }
|
|
|
+
|
|
|
+ if ($objAsset) {
|
|
|
+ $assetModel->where('id', $objAsset['id'])->update(['filename' => $arrInfo["filename"]]);
|
|
|
+ } else {
|
|
|
+ $assetModel->allowField(true)->save($arrInfo);
|
|
|
+ }
|
|
|
+
|
|
|
+ //删除临时文件
|
|
|
+// for ($index = 0; $index < $chunks; $index++) {
|
|
|
+// // echo $targetDir . "{$strFilePath}_{$index}.part";
|
|
|
+// @unlink($targetDir . "{$strFilePath}_{$index}.part");
|
|
|
+// }
|
|
|
+ @rmdir($targetDir);
|
|
|
+
|
|
|
+ if ($storage['type'] != 'Local') { // 增加存储驱动
|
|
|
+ $watermark = cmf_get_plugin_config($storage['type']);
|
|
|
+ $storage = new Storage($storage['type'], $storage['storages'][$storage['type']]);
|
|
|
+
|
|
|
+ if ($needUploadToRemoteStorage) {
|
|
|
+ session_write_close();
|
|
|
+ $result = $storage->upload($arrInfo["file_path"], $uploadPath . $arrInfo["file_path"], $fileType);
|
|
|
+ if (!empty($result)) {
|
|
|
+ return array_merge([
|
|
|
+ 'filepath' => $arrInfo["file_path"],
|
|
|
+ "name" => $arrInfo["filename"],
|
|
|
+ 'id' => $strId,
|
|
|
+ 'preview_url' => cmf_get_root() . '/upload/' . $arrInfo["file_path"],
|
|
|
+ 'url' => cmf_get_root() . '/upload/' . $arrInfo["file_path"],
|
|
|
+ ], $result);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ $previewUrl = $fileType == 'image' ? $storage->getPreviewUrl($arrInfo["file_path"]) : $storage->getFileDownloadUrl($arrInfo["file_path"]);
|
|
|
+ $url = $fileType == 'image' ? $storage->getImageUrl($arrInfo["file_path"], $watermark['styles_watermark']) : $storage->getFileDownloadUrl($arrInfo["file_path"]);
|
|
|
+ //测试ing
|
|
|
+ return [
|
|
|
+ 'filepath' => $arrInfo["file_path"],
|
|
|
+ "name" => $arrInfo["filename"],
|
|
|
+ 'id' => $strId,
|
|
|
+ 'preview_url' => $previewUrl,
|
|
|
+ 'url' => $url,
|
|
|
+ ];
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ return [
|
|
|
+ 'filepath' => $arrInfo["file_path"],
|
|
|
+ "name" => $arrInfo["filename"],
|
|
|
+ 'id' => $strId,
|
|
|
+ 'preview_url' => cmf_get_root() . '/upload/' . $arrInfo["file_path"],
|
|
|
+ 'url' => cmf_get_root() . '/upload/' . $arrInfo["file_path"],
|
|
|
+ ];
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ public function getError()
|
|
|
+ {
|
|
|
+ return $this->error;
|
|
|
+ }
|
|
|
+}
|