• Модуль: main
  • Путь к файлу: ~/bitrix/modules/main/classes/general/file.php
  • Класс: CFile
  • Вызов: CFile::SaveFile
static function SaveFile($arFile, $strSavePath, $forceRandom = false, $skipExtension = false, $dirAdd = '')
{
	$strFileName = GetFileName($arFile["name"] ?? '');	/* filename.gif */

	if(isset($arFile["del"]) && $arFile["del"] <> '')
	{
		static::Delete($arFile["old_file"] ?? 0);
		if($strFileName == '')
			return "NULL";
	}

	if(!isset($arFile["name"]) || $arFile["name"] == '')
	{
		if(isset($arFile["description"]) && intval($arFile["old_file"])>0)
		{
			static::UpdateDesc($arFile["old_file"], $arFile["description"]);
		}
		return false;
	}

	if (isset($arFile["content"]))
	{
		if (!isset($arFile["size"]))
		{
			$arFile["size"] = strlen($arFile["content"]);
		}
	}
	else
	{
		try
		{
			$file = new IOFile(IOPath::convertPhysicalToLogical($arFile["tmp_name"]));
			$arFile["size"] = $file->getSize();
		}
		catch(IOIoException $e)
		{
			$arFile["size"] = 0;
		}
	}

	$arFile["ORIGINAL_NAME"] = $strFileName;

	//translit, replace unsafe chars, etc.
	$strFileName = self::transformName($strFileName, $forceRandom, $skipExtension);

	//transformed name must be valid, check disk quota, etc.
	if (self::validateFile($strFileName, $arFile) !== "")
	{
		return false;
	}

	$arFile["type"] = WebMimeType::normalize($arFile["type"]);

	$original = null;

	$io = CBXVirtualIo::GetInstance();

	$bExternalStorage = false;
	foreach(GetModuleEvents("main", "OnFileSave", true) as $arEvent)
	{
		if(ExecuteModuleEventEx($arEvent, array(&$arFile, $strFileName, $strSavePath, $forceRandom, $skipExtension, $dirAdd)))
		{
			$bExternalStorage = true;
			break;
		}
	}

	if(!$bExternalStorage)
	{
		// we should keep number of files in a folder below 10,000
		// three chars from md5 give us 4096 subdirs

		$upload_dir = COption::GetOptionString("main", "upload_dir", "upload");

		if($forceRandom != true && COption::GetOptionString("main", "save_original_file_name", "N") == "Y")
		{
			//original name
			$subdir = $dirAdd;
			if($subdir == '')
			{
				while(true)
				{
					$random = SecurityRandom::getString(32);
					$subdir = substr(md5($random), 0, 3)."/".$random;

					if(!$io->FileExists($_SERVER["DOCUMENT_ROOT"]."/".$upload_dir."/".$strSavePath."/".$subdir."/".$strFileName))
					{
						break;
					}
				}
			}
			$strSavePath = rtrim($strSavePath, "/")."/".$subdir;
		}
		else
		{
			//random name
			$fileExtension = ($skipExtension == true || ($ext = GetFileExtension($strFileName)) == ''? '' : ".".$ext);
			while(true)
			{
				$subdir = substr(md5($strFileName), 0, 3);
				$strSavePath = rtrim($strSavePath, "/")."/".$subdir;

				if(!$io->FileExists($_SERVER["DOCUMENT_ROOT"]."/".$upload_dir."/".$strSavePath."/".$strFileName))
				{
					break;
				}

				//try the new name
				$strFileName = SecurityRandom::getString(32).$fileExtension;
			}
		}

		$arFile["SUBDIR"] = $strSavePath;
		$arFile["FILE_NAME"] = $strFileName;

		$dirName = $_SERVER["DOCUMENT_ROOT"]."/".$upload_dir."/".$strSavePath."/";
		$physicalFileName = $io->GetPhysicalName($dirName.$strFileName);

		CheckDirPath($dirName);

		if(is_set($arFile, "content"))
		{
			if(file_put_contents($physicalFileName, $arFile["content"]) === false)
			{
				return false;
			}
		}
		else
		{
			if(!copy($arFile["tmp_name"], $physicalFileName) && !move_uploaded_file($arFile["tmp_name"], $physicalFileName))
			{
				return false;
			}
		}

		if(isset($arFile["old_file"]))
		{
			static::Delete($arFile["old_file"]);
		}

		@chmod($physicalFileName, BX_FILE_PERMISSIONS);

		//flash is not an image
		$flashEnabled = !static::IsImage($arFile["ORIGINAL_NAME"], $arFile["type"]);

		$image = new FileImage($physicalFileName);

		$imgInfo = $image->getInfo($flashEnabled);

		if($imgInfo)
		{
			$arFile["WIDTH"] = $imgInfo->getWidth();
			$arFile["HEIGHT"] = $imgInfo->getHeight();

			if ($imgInfo->getFormat() == FileImage::FORMAT_JPEG && empty($arFile['no_rotate']) && !$image->exceedsMaxSize())
			{
				$exifData = $image->getExifData();
				if (isset($exifData['Orientation']) && $exifData['Orientation'] > 1)
				{
					if($image->load())
					{
						if($image->autoRotate($exifData['Orientation']))
						{
							$quality = COption::GetOptionString('main', 'image_resize_quality');
							if($image->save($quality))
							{
								// update width and height
								$arFile['WIDTH'] = $image->getWidth();
								$arFile['HEIGHT'] = $image->getHeight();
								$arFile['size'] = filesize($physicalFileName);
							}
						}
					}
				}
			}
		}
		else
		{
			$arFile["WIDTH"] = 0;
			$arFile["HEIGHT"] = 0;
		}

		//calculate a hash for the control of duplicates
		$arFile["FILE_HASH"] = static::CalculateHash($physicalFileName, $arFile["size"]);

		//control of duplicates
		if ($arFile["FILE_HASH"] <> '')
		{
			$lockId = static::lockFileHash($arFile["size"], $arFile["FILE_HASH"]);
			$original = static::FindDuplicate($arFile["size"], $arFile["FILE_HASH"]);

			if($original !== null)
			{
				//points to the original's physical path
				$arFile["SUBDIR"] = $original->getFile()->getSubdir();
				$arFile["FILE_NAME"] = $original->getFile()->getFileName();

				$originalPath = $_SERVER["DOCUMENT_ROOT"]."/".$upload_dir."/".$arFile["SUBDIR"]."/".$arFile["FILE_NAME"];

				if($physicalFileName <> $io->GetPhysicalName($originalPath))
				{
					unlink($physicalFileName);
					try
					{
						rmdir($io->GetPhysicalName($dirName));
					}
					catch (ErrorException $exception)
					{
						// Ignore a E_WARNING Error
					}
				}
			}
		}
	}
	else
	{
		//from clouds
		if(isset($arFile["original_file"]) && $arFile["original_file"] instanceof InternalEO_FileHash)
		{
			$original = $arFile["original_file"];
		}
	}

	if($arFile["WIDTH"] == 0 || $arFile["HEIGHT"] == 0)
	{
		//mock image because we got false from CFile::GetImageSize()
		if(strpos($arFile["type"], "image/") === 0 && $arFile["type"] <> 'image/svg+xml')
		{
			$arFile["type"] = "application/octet-stream";
		}
	}

	/****************************** QUOTA ******************************/
	if (COption::GetOptionInt("main", "disk_space") > 0 && $original === null)
	{
		CDiskQuota::updateDiskQuota("file", $arFile["size"], "insert");
	}
	/****************************** QUOTA ******************************/

	$NEW_IMAGE_ID = static::DoInsert(array(
		"HEIGHT" => $arFile["HEIGHT"],
		"WIDTH" => $arFile["WIDTH"],
		"FILE_SIZE" => $arFile["size"],
		"CONTENT_TYPE" => $arFile["type"],
		"SUBDIR" => $arFile["SUBDIR"],
		"FILE_NAME" => $arFile["FILE_NAME"],
		"MODULE_ID" => $arFile["MODULE_ID"] ?? '',
		"ORIGINAL_NAME" => $arFile["ORIGINAL_NAME"],
		"DESCRIPTION" => ($arFile["description"] ?? ''),
		"HANDLER_ID" => ($arFile["HANDLER_ID"] ?? ''),
		"EXTERNAL_ID" => ($arFile["external_id"] ?? md5(mt_rand())),
		"FILE_HASH" => ($original === null? $arFile["FILE_HASH"] : ''),
	));

	if (isset($lockId))
	{
		static::unlockFileHash($lockId);
	}

	if($original !== null)
	{
		//save information about the duplicate for future use (on deletion)
		static::AddDuplicate($original->getFileId(), $NEW_IMAGE_ID, false);
	}

	static::CleanCache($NEW_IMAGE_ID);

	return $NEW_IMAGE_ID;
}