<?php
/**
  * @method 上传
  * @author soul
  * @copyright 2017/6/15
  */
class OthUpload
{
	/**
	  * @method 大文件上传
	  * @param  $optArr [
			'dir'=>默认在home/tmp/,
			'filemd5'=>文件的MD5,
			'filename'=>'自定义前缀',
			'name'=>文件名
		]
	  * @author soul
	  * @copyright 2017/6/15
	  * @return 
		{"status":true,"data":{"oldname":"huari.zip","ext":"zip","filemd5":"03872305d4fd51e53fd1ea9a4f9f6146","filename":"149894_03872305d4fd51e53fd1ea9a4f9f6146","haschunk":[0],"status":"uploaded","msg":"文件已上传完成。"},"code":0}
	  */
	public function uploadBigFile($optArr)
	{
		$arr = [];
		if(empty($optArr['filemd5']) || empty($optArr['name']))
		{
			$arr['status'] = 'error';
			$arr['msg'] = '获取文件失败，请重新上传。';
			return LibFc::ReturnData(true, $arr);
		}

		if(empty($optArr['dir'])) $optArr['dir'] = mvc::$cfg['ROOT_BIGFILE'];

		//初始化路径
		$arr['oldname'] = !empty($optArr['name'])? $optArr['name']: $_FILES['file']['name'];
		$pathInfo = pathinfo($arr['oldname']);
		$arr['ext'] = $pathInfo['extension'];
		$arr['filemd5'] = $optArr['filemd5'];
		$arr['filename'] = $optArr['filemd5'];
		if(!empty($optArr['filename'])) $arr['filename'] = $optArr['filename'].'_'.$arr['filename'];
		$saveFile = $optArr['dir'].$arr['filename'].'.'.$arr['ext'];

		if(file_exists($saveFile))
		{
			$arr['status'] = 'uploaded';
			$arr['haschunk'] = ['ALL'];
			$arr['msg'] = '文件已上传完成。';
			return LibFc::ReturnData(true, $arr);
		}

		$chunks = !empty($optArr['chunks']) ? (int) $optArr['chunks']: 1;
		$chunk = !empty($optArr['chunk']) ? (int) $optArr['chunk']: 0;

		//临时目录
		$tmpDir = $optArr['dir'].$arr['filename'].'/';
		@mkdir($tmpDir, 0777, true);
		
		//检查分片哪些存在
		if(!empty($optArr['act']) && $optArr['act'] == 'check')
		{
			$arr['haschunk'] = [];
			for($i = 0; $i <= 1000; $i++)
			{
				if(file_exists($tmpDir.$i.'.part'))
				{
					$arr['haschunk'][] = $i;
				}
			}
			$arr['status'] = 'check';
			$arr['msg'] = '检查断点完毕！';
			return LibFc::ReturnData(true, $arr);
		}

		//接收分片文件并存储
		if($_SERVER['REQUEST_METHOD'] != 'POST' || $_FILES['file']['error'] || !is_uploaded_file($_FILES['file']['tmp_name'])) 
		{
			$arr['status'] = 'error';
			$arr['msg'] = '获取文件失败，请重新上传。';
			return LibFc::ReturnData(true, $arr);
		}
		
		$tmpFile = $tmpDir.$chunk.'.parttmp';
		if (!$whandle = fopen($tmpFile, 'wb'))
		{
			$arr['status'] = 'error';
			$arr['msg'] = '打开文件失败，请重新上传。';
			return LibFc::ReturnData(true, $arr);
		}

		if (!$rhandle = fopen($_FILES['file']['tmp_name'], 'rb'))
		{
			$arr['status'] = 'error';
			$arr['msg'] = '打开文件失败，请重新上传。';
			return LibFc::ReturnData(true, $arr);
		}
		
		while($buff = fread($rhandle, 4096))
		{
			fwrite($whandle, $buff);
		}
		fclose($whandle);
		fclose($rhandle);
		rename($tmpFile, substr($tmpFile, 0, -3));

		//检测分片是否已经传输完毕
		$arr['haschunk'] = [];
		for($i = 0; $i < $chunks; $i++)
		{
			if(file_exists($tmpDir.$i.'.part'))
			{
				$arr['haschunk'][] = $i;
			}
		}

		//合并文件
		if(count($arr['haschunk']) == $chunks)                                                                             
		{
			if (!$whandle = fopen($saveFile, 'wb'))
			{
				$arr['status'] = 'error';
				$arr['msg'] = '打开文件失败，请重新上传。';
				return LibFc::ReturnData(true, $arr);
			}
			
			if(flock($whandle, LOCK_EX)) 
			{
				for($i = 0; $i < $chunks; $i++)
				{
					if(!$rhandle = fopen($tmpDir.$i.'.part', 'rb')) 
					{
						$arr['status'] = 'error';
						$arr['msg'] = '打开文件失败，请重新上传。';
						break;
					}
					while ($buff = fread($rhandle, 4096))
					{
						fwrite($whandle, $buff);
					}
					fclose($rhandle);
				}
				flock($whandle, LOCK_UN);
			}
			fclose($whandle);

			//校验合并后文件的MD5
			if($arr['status'] == 'uploaded' && md5(file_get_contents($saveFile)) != $arr['filemd5'])
			{
				@unlink($saveFile);
				//和原文件不一样，重传
				$arr['status'] = 'reupload';
				$arr['msg'] = '获取文件失败，请重新上传。';
			}
			else
			{
				$arr['status'] = 'uploaded';
				$arr['msg'] = '文件已上传完成。';
			}
			for($i = 0; $i < $chunks; $i++)
			{
				@unlink($tmpDir.$i.'.part');
			}
			@rmdir($tmpDir);
		}
		else
		{
			$arr['status'] = 'uploading';
			$arr['msg'] = '文件上传中，请稍等。';
		}
		return LibFc::ReturnData(true, $arr);
	}
}
?>