Upload.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353
  1. <?php
  2. /**
  3. * lvzheAdmin [a web admin based ThinkPHP5]
  4. * @author xiekunyu<raingad@foxmail.com>
  5. */
  6. namespace app\common\controller;
  7. use app\BaseController;
  8. use app\enterprise\model\{File as FileModel,Message,User,Emoji};
  9. use app\manage\model\{Config};
  10. use think\facade\Filesystem;
  11. use think\facade\Request;
  12. use think\File;
  13. use FFMpeg\FFMpeg;
  14. use FFMpeg\FFProbe;
  15. use FFMpeg\Coordinate\TimeCode;
  16. class Upload extends BaseController
  17. {
  18. protected $middleware = ['checkAuth'];
  19. protected $disk='';
  20. protected $url='';
  21. public function __construct()
  22. {
  23. parent::__construct(app());
  24. $this->disk=env('filesystem.driver','local');
  25. $this->url=getDiskUrl().'/';
  26. }
  27. /**
  28. * 文件上传
  29. */
  30. public function upload($data,$path,$prefix = "",$fileObj = true)
  31. {
  32. $message=$data['message'] ?? '';
  33. if($message){
  34. $message=json_decode($message,true);
  35. }
  36. $uid=request()->userInfo['user_id'] ?? 1;
  37. if($fileObj){
  38. $filePath = $path;
  39. }else{
  40. $filePath = new File($path);
  41. }
  42. $info=$this->getFileInfo($filePath,$path,$fileObj);
  43. if($info['ext']=='' && $message){
  44. $pathInfo = pathinfo($message['fileName'] ?? '');
  45. $info['ext'] = $pathInfo['extension'];
  46. $info['name'] =$message['fileName'] ?? '';
  47. }
  48. $conf=Config::getFieldValue('fileUpload');
  49. if($conf['size']*1024*1024 < $info['size']){
  50. return shutdown(lang('file.uploadLimit',['size'=>$conf['size']]));
  51. }
  52. // 兼容uniapp文件上传
  53. if($info['ext']=='' && isset($data['ext'])){
  54. $info['ext']=$data['ext'];
  55. }
  56. $info['ext']=strtolower($info['ext']);
  57. if(!in_array($info['ext'],$conf['fileExt'])){
  58. return shutdown(lang('file.typeNotSupport'));
  59. }
  60. $fileType=getFileType($info['ext']);
  61. $imageInfo=[];
  62. if($fileType==2){
  63. $filecate="image";
  64. $imageInfo=$this->getImageSizeInfo($info['path']);
  65. }elseif($fileType==3){
  66. $msgType=$message['type'] ?? '';
  67. // 如果是语音消息,类型才为语音,否者为文件,主要是兼容发送音频文件
  68. if($msgType=='voice'){
  69. $filecate="voice";
  70. }else{
  71. $filecate="file";
  72. }
  73. }elseif($fileType==4){
  74. $filecate="video";
  75. }else{
  76. $filecate="file";
  77. }
  78. if(!$prefix){
  79. $prefix=$filecate.'/'.date('Y-m-d').'/'.$uid."/";
  80. }
  81. $name=str_replace('.'.$info['ext'],'',$info['name']);
  82. $file=FileModel::where(['md5'=>$info['md5']])->find();
  83. // 判断文件是否存在,如果有则不再上传
  84. if(!$file){
  85. $newName = uniqid() . '.' . $info['ext'];
  86. $object = $prefix . $newName;
  87. if($this->disk=='local'){
  88. $object='storage/'.$object;
  89. }
  90. Filesystem::disk($this->disk)->putFileAs($prefix, $filePath, $newName);
  91. }else{
  92. $object = $file['src'];
  93. }
  94. //判断文件所在的目录是否存在,不存在再创建目录
  95. // $dir=dirname($object);
  96. // if(!Filesystem::disk($this->disk)->has($dir)){
  97. // Filesystem::disk($this->disk)->createDir($dir);
  98. // }
  99. // 把左边的/去掉再加上,避免有些有/有些没有
  100. $object='/'.ltrim($object,'/');
  101. $ret = [
  102. "src" => $object,
  103. "name" => $name,
  104. "cate" => $fileType,
  105. "size" => $info['size'],
  106. "md5" => $info['md5'],
  107. "file_type" => $info['mime'],
  108. "ext" => $info['ext'],
  109. "type" =>2,
  110. 'user_id'=>$uid,
  111. 'videoInfo'=>$imageInfo
  112. ];
  113. if($message){
  114. // 如果发送的文件是图片、视频、音频则将消息类型改为对应的类型
  115. if(in_array($fileType,[2,3,4])){
  116. $message['type']=$filecate;
  117. }
  118. if($message['type']=='image'){
  119. $message['extends']=$imageInfo;
  120. }
  121. // 自动获取视频第一帧,视频并且是使用的阿里云
  122. if($message['type']=='video'){
  123. $videoInfo=$this->getVideoCover($filePath);
  124. if($videoInfo){
  125. $extends=$videoInfo['videoInfo'];
  126. $extends['poster']=$this->url.$videoInfo['src'];
  127. $message['extends']=$extends;
  128. }else{
  129. $message['extends']['poster']=getMainHost().'/static/common/img/video.png';
  130. }
  131. // if($this->disk=='aliyun'){
  132. // $message['extends']['poster']=$this->url.$ret['src'].'?x-oss-process=video/snapshot,t_1000,m_fast,w_800,f_png';
  133. // }else{
  134. // $message['extends']['poster']=getMainHost().'/static/common/img/video.png';
  135. // }
  136. }
  137. $newFile=new FileModel;
  138. // 录音就不保存了
  139. if($message['type']!='voice'){
  140. $newFile->save($ret);
  141. }
  142. $message['content']=$ret['src'];
  143. $message['file_id']=$newFile->file_id ?? 0;
  144. $message['file_cate']=$fileType;
  145. $message['file_size']=$info['size'];
  146. $message['file_name']= $name.'.'.$info['ext'];
  147. $message['user_id']= $uid;
  148. $messageModel=new Message();
  149. $data=$messageModel->sendMessage($message,$this->globalConfig);
  150. if(!$data){
  151. return shutdown($messageModel->getError());
  152. }
  153. return $data;
  154. }else{
  155. return $ret;
  156. }
  157. }
  158. // 上传一般文件
  159. public function uploadFile(){
  160. $param=$this->request->param();
  161. try{
  162. $file=request()->file('file');
  163. $info=$this->upload($param,$file);
  164. return success(lang('file.uploadOk'),$info);
  165. } catch(\Exception $e) {
  166. return error($e->getMessage().$e->getLine());
  167. }
  168. }
  169. // 获取上传文件的信息
  170. protected function getFileInfo($file,$path,$isObj=false){
  171. $info= [
  172. 'path'=>$file->getRealPath(),
  173. 'size'=>$file->getSize(),
  174. 'mime'=>$file->getMime(),
  175. 'ext'=>$file->extension(),
  176. 'md5'=>$file->md5(),
  177. ];
  178. if($isObj){
  179. $info['name']=$file->getOriginalName();
  180. }else{
  181. // 根据路径获取文件名
  182. $pathInfo = pathinfo($path);
  183. $info['name'] = $pathInfo['basename'];
  184. }
  185. return $info;
  186. }
  187. // 上传图片
  188. public function uploadImage(){
  189. $param=request::param();
  190. try{
  191. $file=request()->file('file');
  192. $info=$this->upload($param,$file,'image/'.date('Y-m-d').'/');
  193. $url=$this->url.$info['src'];
  194. return success(lang('file.uploadOk'),$url);
  195. } catch(\Exception $e) {
  196. return error($e->getMessage());
  197. }
  198. }
  199. // 普通上传头像
  200. public function uploadAvatar(){
  201. $param=request::param();
  202. try{
  203. $file=request()->file('file');
  204. $uid=request()->userInfo['user_id'];
  205. $info=$this->upload($param,$file,'avatar/'.$uid.'/');
  206. User::where(['user_id'=>$uid])->update(['avatar'=>$info['src']]);
  207. $url=$this->url.$info['src'];
  208. return success(lang('file.uploadOk'),$url);
  209. } catch(\Exception $e) {
  210. return error($e->getMessage());
  211. }
  212. }
  213. // 服务器上传头像
  214. public function uploadLocalAvatar($file,$param,$uid){
  215. try{
  216. $info=$this->upload($param,$file,'avatar/'.$uid.'/',false);
  217. return $info['src'];
  218. } catch(\Exception $e) {
  219. return $e->getMessage().$e->getLine();
  220. }
  221. }
  222. // 上传表情
  223. public function uploadEmoji(){
  224. $param=request::param();
  225. try{
  226. $file=request()->file('file');
  227. $filePath = $file;
  228. $uid=request()->userInfo['user_id'] ?? 1;
  229. $info=$this->getFileInfo($filePath,$file,true);
  230. if($info['ext']==''){
  231. $pathInfo = pathinfo($message['fileName'] ?? '');
  232. $info['ext'] = $pathInfo['extension'];
  233. $info['name'] =$message['fileName'] ?? '';
  234. }
  235. // 表情不能大于1m
  236. if(2*1024*1024 < $info['size']){
  237. return shutdown(lang('file.uploadLimit',['size'=>2]));
  238. }
  239. // 兼容uniapp文件上传
  240. if($info['ext']=='' && isset($param['ext'])){
  241. $info['ext']=$param['ext'];
  242. }
  243. $info['ext']=strtolower($info['ext']);
  244. if(!in_array($info['ext'],['jpg','jpeg','gif','png'])){
  245. return shutdown(lang('file.typeNotSupport'));
  246. }
  247. $prefix='emoji/'.$uid.'/';
  248. $name=str_replace('.'.$info['ext'],'',$info['name']);
  249. $fileInfo=FileModel::where(['md5'=>$info['md5']])->find();
  250. // 判断文件是否存在,如果有则不再上传
  251. if(!$fileInfo){
  252. $newName = uniqid() . '.' . $info['ext'];
  253. $object = $prefix . $newName;
  254. if($this->disk=='local'){
  255. $object='storage/'.$object;
  256. }
  257. Filesystem::disk($this->disk)->putFileAs($prefix, $filePath, $newName);
  258. $ret = [
  259. "src" => $object,
  260. "name" => $name,
  261. "cate" => 1,
  262. "size" => $info['size'],
  263. "md5" => $info['md5'],
  264. "file_type" => $info['mime'],
  265. "ext" => $info['ext'],
  266. "type" =>2,
  267. 'user_id'=>$uid,
  268. ];
  269. $fileInfo=new FileModel;
  270. $fileInfo->save($ret);
  271. }else{
  272. $object = $fileInfo->src;
  273. }
  274. // 把左边的/去掉再加上,避免有些有/有些没有
  275. $object='/'.ltrim($object,'/');
  276. $emojiInfo=[
  277. 'user_id' => $uid,
  278. "src" => $object,
  279. "name" => $name,
  280. "type" => 2,
  281. "file_id" => $fileInfo->file_id,
  282. ];
  283. Emoji::create($emojiInfo);
  284. return success('',$this->url.$object);
  285. } catch(\Exception $e) {
  286. return $e->getMessage().$e->getLine();
  287. }
  288. }
  289. // 获取图片的尺寸
  290. protected function getImageSizeInfo($file){
  291. $extends=[];
  292. // 如果图片获取图片的尺寸
  293. $imageSize = getimagesize($file);
  294. $extends['width']=$imageSize[0];
  295. $extends['height']=$imageSize[1];
  296. // 如果宽大于高则为横图,宽度填充模式,否则为竖图,高度填充模式
  297. if($imageSize[0]>=$imageSize[1]){
  298. $extends['fixMode']=1; // 宽度填充
  299. }else{
  300. $extends['fixMode']=2; // 高度填充
  301. }
  302. if($imageSize[0]<200 && $imageSize[1]<240){
  303. $extends['fixMode']=3; // 小图
  304. }
  305. return $extends;
  306. }
  307. // 获取视频封面
  308. public function getVideoCover($filePath){
  309. $fileName=pathinfo($filePath,PATHINFO_FILENAME).'.jpg';
  310. $ffmpegPath=env('ffmpeg.bin_path','');
  311. if(!$ffmpegPath){
  312. return false;
  313. }
  314. $path=array(
  315. 'ffmpeg.binaries' => $ffmpegPath.'/ffmpeg',
  316. 'ffprobe.binaries' => $ffmpegPath.'/ffprobe',
  317. 'timeout' => 3600, // 进程超时时间
  318. 'ffmpeg.threads' => 12, // FFMpeg应使用的线程数
  319. );
  320. $ffmpeg = FFMpeg::create($path);
  321. $ffprobe = FFProbe::create($path);
  322. $duration=$ffprobe->format($filePath)->get('duration');// 获取 duration 属性
  323. $video = $ffmpeg->open($filePath);
  324. $frame = $video->frame(TimeCode::fromSeconds(1));
  325. $tempPath=root_path().'public/temp';
  326. $savePath=$tempPath. '/' .$fileName;
  327. $frame->save($savePath);
  328. $info=$this->upload([],$savePath,'cover/'.date('Y-m-d').'/',false);
  329. $info['videoInfo']['duration']= ceil($duration);
  330. unlink($savePath);
  331. return $info;
  332. }
  333. }