Upload.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348
  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::where(['field'=>'fileUpload'])->value('val');
  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. $object='/'.ltrim($object,'/');
  96. $ret = [
  97. "src" => $object,
  98. "name" => $name,
  99. "cate" => $fileType,
  100. "size" => $info['size'],
  101. "md5" => $info['md5'],
  102. "file_type" => $info['mime'],
  103. "ext" => $info['ext'],
  104. "type" =>2,
  105. 'user_id'=>$uid,
  106. 'videoInfo'=>$imageInfo
  107. ];
  108. if($message){
  109. // 如果发送的文件是图片、视频、音频则将消息类型改为对应的类型
  110. if(in_array($fileType,[2,3,4])){
  111. $message['type']=$filecate;
  112. }
  113. if($message['type']=='image'){
  114. $message['extends']=$imageInfo;
  115. }
  116. // 自动获取视频第一帧,视频并且是使用的阿里云
  117. if($message['type']=='video'){
  118. $videoInfo=$this->getVideoCover($filePath);
  119. if($videoInfo){
  120. $extends=$videoInfo['videoInfo'];
  121. $extends['poster']=$this->url.$videoInfo['src'];
  122. $message['extends']=$extends;
  123. }else{
  124. $message['extends']['poster']=getMainHost().'/static/common/img/video.png';
  125. }
  126. // if($this->disk=='aliyun'){
  127. // $message['extends']['poster']=$this->url.$ret['src'].'?x-oss-process=video/snapshot,t_1000,m_fast,w_800,f_png';
  128. // }else{
  129. // $message['extends']['poster']=getMainHost().'/static/common/img/video.png';
  130. // }
  131. }
  132. $newFile=new FileModel;
  133. // 录音就不保存了
  134. if($message['type']!='voice'){
  135. $newFile->save($ret);
  136. }
  137. $message['content']=$ret['src'];
  138. $message['file_id']=$newFile->file_id ?? 0;
  139. $message['file_cate']=$fileType;
  140. $message['file_size']=$info['size'];
  141. $message['file_name']= $name.'.'.$info['ext'];
  142. $message['user_id']= $uid;
  143. $messageModel=new Message();
  144. $data=$messageModel->sendMessage($message,$this->globalConfig);
  145. if(!$data){
  146. return shutdown($messageModel->getError());
  147. }
  148. return $data;
  149. }else{
  150. return $ret;
  151. }
  152. }
  153. // 上传一般文件
  154. public function uploadFile(){
  155. $param=$this->request->param();
  156. try{
  157. $file=request()->file('file');
  158. $info=$this->upload($param,$file);
  159. return success(lang('file.uploadOk'),$info);
  160. } catch(\Exception $e) {
  161. return error($e->getMessage().$e->getLine());
  162. }
  163. }
  164. // 获取上传文件的信息
  165. protected function getFileInfo($file,$path,$isObj=false){
  166. $info= [
  167. 'path'=>$file->getRealPath(),
  168. 'size'=>$file->getSize(),
  169. 'mime'=>$file->getMime(),
  170. 'ext'=>$file->extension(),
  171. 'md5'=>$file->md5(),
  172. ];
  173. if($isObj){
  174. $info['name']=$file->getOriginalName();
  175. }else{
  176. // 根据路径获取文件名
  177. $pathInfo = pathinfo($path);
  178. $info['name'] = $pathInfo['basename'];
  179. }
  180. return $info;
  181. }
  182. // 上传图片
  183. public function uploadImage(){
  184. $param=request::param();
  185. try{
  186. $file=request()->file('file');
  187. $info=$this->upload($param,$file,'image/'.date('Y-m-d').'/');
  188. $url=$this->url.$info['src'];
  189. return success(lang('file.uploadOk'),$url);
  190. } catch(\Exception $e) {
  191. return error($e->getMessage());
  192. }
  193. }
  194. // 普通上传头像
  195. public function uploadAvatar(){
  196. $param=request::param();
  197. try{
  198. $file=request()->file('file');
  199. $uid=request()->userInfo['user_id'];
  200. $info=$this->upload($param,$file,'avatar/'.$uid.'/');
  201. User::where(['user_id'=>$uid])->update(['avatar'=>$info['src']]);
  202. $url=$this->url.$info['src'];
  203. return success(lang('file.uploadOk'),$url);
  204. } catch(\Exception $e) {
  205. return error($e->getMessage());
  206. }
  207. }
  208. // 服务器上传头像
  209. public function uploadLocalAvatar($file,$param,$uid){
  210. try{
  211. $info=$this->upload($param,$file,'avatar/'.$uid.'/',false);
  212. return $info['src'];
  213. } catch(\Exception $e) {
  214. return $e->getMessage().$e->getLine();
  215. }
  216. }
  217. // 上传表情
  218. public function uploadEmoji(){
  219. $param=request::param();
  220. try{
  221. $file=request()->file('file');
  222. $filePath = $file;
  223. $uid=request()->userInfo['user_id'] ?? 1;
  224. $info=$this->getFileInfo($filePath,$file,true);
  225. if($info['ext']==''){
  226. $pathInfo = pathinfo($message['fileName'] ?? '');
  227. $info['ext'] = $pathInfo['extension'];
  228. $info['name'] =$message['fileName'] ?? '';
  229. }
  230. // 表情不能大于1m
  231. if(2*1024*1024 < $info['size']){
  232. return shutdown(lang('file.uploadLimit',['size'=>2]));
  233. }
  234. // 兼容uniapp文件上传
  235. if($info['ext']=='' && isset($param['ext'])){
  236. $info['ext']=$param['ext'];
  237. }
  238. $info['ext']=strtolower($info['ext']);
  239. if(!in_array($info['ext'],['jpg','jpeg','gif','png'])){
  240. return shutdown(lang('file.typeNotSupport'));
  241. }
  242. $prefix='emoji/'.$uid.'/';
  243. $name=str_replace('.'.$info['ext'],'',$info['name']);
  244. $fileInfo=FileModel::where(['md5'=>$info['md5']])->find();
  245. // 判断文件是否存在,如果有则不再上传
  246. if(!$fileInfo){
  247. $newName = uniqid() . '.' . $info['ext'];
  248. $object = $prefix . $newName;
  249. if($this->disk=='local'){
  250. $object='storage/'.$object;
  251. }
  252. Filesystem::disk($this->disk)->putFileAs($prefix, $filePath, $newName);
  253. $ret = [
  254. "src" => $object,
  255. "name" => $name,
  256. "cate" => 1,
  257. "size" => $info['size'],
  258. "md5" => $info['md5'],
  259. "file_type" => $info['mime'],
  260. "ext" => $info['ext'],
  261. "type" =>2,
  262. 'user_id'=>$uid,
  263. ];
  264. $fileInfo=new FileModel;
  265. $fileInfo->save($ret);
  266. }else{
  267. $object = $fileInfo->src;
  268. }
  269. // 把左边的/去掉再加上,避免有些有/有些没有
  270. $object='/'.ltrim($object,'/');
  271. $emojiInfo=[
  272. 'user_id' => $uid,
  273. "src" => $object,
  274. "name" => $name,
  275. "type" => 2,
  276. "file_id" => $fileInfo->file_id,
  277. ];
  278. Emoji::create($emojiInfo);
  279. return success('',$this->url.$object);
  280. } catch(\Exception $e) {
  281. return $e->getMessage().$e->getLine();
  282. }
  283. }
  284. // 获取图片的尺寸
  285. protected function getImageSizeInfo($file){
  286. $extends=[];
  287. // 如果图片获取图片的尺寸
  288. $imageSize = getimagesize($file);
  289. $extends['width']=$imageSize[0];
  290. $extends['height']=$imageSize[1];
  291. // 如果宽大于高则为横图,宽度填充模式,否则为竖图,高度填充模式
  292. if($imageSize[0]>=$imageSize[1]){
  293. $extends['fixMode']=1; // 宽度填充
  294. }else{
  295. $extends['fixMode']=2; // 高度填充
  296. }
  297. if($imageSize[0]<200 && $imageSize[1]<240){
  298. $extends['fixMode']=3; // 小图
  299. }
  300. return $extends;
  301. }
  302. // 获取视频封面
  303. public function getVideoCover($filePath){
  304. $fileName=pathinfo($filePath,PATHINFO_FILENAME).'.jpg';
  305. $ffmpegPath=env('ffmpeg.bin_path','');
  306. if(!$ffmpegPath){
  307. return false;
  308. }
  309. $path=array(
  310. 'ffmpeg.binaries' => $ffmpegPath.'/ffmpeg',
  311. 'ffprobe.binaries' => $ffmpegPath.'/ffprobe',
  312. 'timeout' => 3600, // 进程超时时间
  313. 'ffmpeg.threads' => 12, // FFMpeg应使用的线程数
  314. );
  315. $ffmpeg = FFMpeg::create($path);
  316. $ffprobe = FFProbe::create($path);
  317. $duration=$ffprobe->format($filePath)->get('duration');// 获取 duration 属性
  318. $video = $ffmpeg->open($filePath);
  319. $frame = $video->frame(TimeCode::fromSeconds(1));
  320. $tempPath=root_path().'public/temp';
  321. $savePath=$tempPath. '/' .$fileName;
  322. $frame->save($savePath);
  323. $info=$this->upload([],$savePath,'cover/'.date('Y-m-d').'/',false);
  324. $info['videoInfo']['duration']= ceil($duration);
  325. unlink($savePath);
  326. return $info;
  327. }
  328. }