智慧教务系统
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

247 lines
8.6 KiB

<?php
// +----------------------------------------------------------------------
// | Niucloud-admin 企业快速开发的多应用管理平台
// +----------------------------------------------------------------------
// | 官方网址:https://www.niucloud.com
// +----------------------------------------------------------------------
// | niucloud团队 版权所有 开源版本可自由商用
// +----------------------------------------------------------------------
// | Author: Niucloud Team
// +----------------------------------------------------------------------
namespace app\service\admin\upload;
use app\service\core\sys\CoreConfigService;
use core\base\BaseAdminService;
use core\exception\AdminException;
/**
* 对象存储直传服务类
*/
class DirectUploadService extends BaseAdminService
{
/**
* 获取上传凭证
* @param string $file_type 文件类型
* @param string $file_name 文件名
* @return array
*/
public function getUploadCredentials($file_type = 'image', $file_name = '')
{
// 获取存储配置
$config_data = (new CoreConfigService())->getConfig('STORAGE');
if (empty($config_data) || empty($config_data['value'])) {
throw new AdminException('存储配置未设置');
}
$storage_config = $config_data['value'];
if (is_string($storage_config)) {
$storage_config = json_decode($storage_config, true);
}
if (empty($storage_config)) {
throw new AdminException('存储配置格式错误');
}
$default_storage = $storage_config['default'] ?? 'local';
if ($default_storage == 'local') {
throw new AdminException('本地存储不支持直传');
}
if (!isset($storage_config[$default_storage])) {
throw new AdminException('存储配置错误');
}
$config = $storage_config[$default_storage];
// 生成文件路径
$file_path = $this->generateFilePath($file_type, $file_name);
// 根据不同存储类型获取凭证
switch ($default_storage) {
case 'tencent':
return $this->getTencentCredentials($config, $file_path, $file_type);
case 'aliyun':
return $this->getAliyunCredentials($config, $file_path, $file_type);
default:
throw new AdminException('不支持的存储类型');
}
}
/**
* 获取腾讯云COS直传凭证
* @param array $config 配置信息
* @param string $file_path 文件路径
* @param string $file_type 文件类型
* @return array
*/
private function getTencentCredentials($config, $file_path, $file_type)
{
// 检查必要的配置
$required_keys = ['bucket', 'region', 'access_key', 'secret_key'];
foreach ($required_keys as $key) {
if (empty($config[$key])) {
throw new AdminException("腾讯云配置缺少必要参数: {$key}");
}
}
$current_time = time();
$expired = $current_time + 3600; // 1小时过期
$key_time = $current_time . ';' . $expired;
// 生成POST策略 (腾讯云COS对象格式)
$policy = [
'expiration' => gmdate('Y-m-d\TH:i:s.000\Z', $expired),
'conditions' => [
// key路径限制 - starts-with格式
['starts-with', '$key', dirname($file_path) . '/'],
// 签名字段 - 对象格式 (腾讯云COS要求)
['$q-sign-algorithm' => 'sha1'],
['$q-ak' => $config['access_key']],
['$q-key-time' => $key_time]
]
];
// 根据文件类型设置大小限制
$max_size = $this->getMaxFileSize($file_type);
if ($max_size > 0) {
$policy['conditions'][] = ['content-length-range', 0, $max_size];
}
$policy_encoded = base64_encode(json_encode($policy, JSON_UNESCAPED_SLASHES));
// 腾讯云COS POST签名算法
$sign_key = hash_hmac('sha1', $key_time, $config['secret_key']);
$string_to_sign = $policy_encoded;
$signature = hash_hmac('sha1', $string_to_sign, $sign_key);
return [
'storage_type' => 'tencent',
'upload_url' => "https://{$config['bucket']}.cos.{$config['region']}.myqcloud.com",
'credentials' => [
'policy' => $policy_encoded,
'q-sign-algorithm' => 'sha1',
'q-ak' => $config['access_key'],
'q-key-time' => $key_time,
'q-signature' => $signature,
],
'file_path' => $file_path,
'domain' => $config['domain'] ?? "https://{$config['bucket']}.cos.{$config['region']}.myqcloud.com",
'max_size' => $max_size,
'expired' => $expired
];
}
/**
* 获取阿里云OSS直传凭证
* @param array $config 配置信息
* @param string $file_path 文件路径
* @param string $file_type 文件类型
* @return array
*/
private function getAliyunCredentials($config, $file_path, $file_type)
{
// 检查必要的配置
$required_keys = ['bucket', 'endpoint', 'access_key', 'secret_key'];
foreach ($required_keys as $key) {
if (empty($config[$key])) {
throw new AdminException("阿里云配置缺少必要参数: {$key}");
}
}
$expired = time() + 3600; // 1小时过期
$dir = dirname($file_path) . '/';
// 生成策略
$policy = [
'expiration' => gmdate('Y-m-d\TH:i:s.000\Z', $expired),
'conditions' => [
['starts-with' => '$key', $dir],
]
];
// 根据文件类型设置大小限制
$max_size = $this->getMaxFileSize($file_type);
if ($max_size > 0) {
$policy['conditions'][] = ['content-length-range', 0, $max_size];
}
$policy_encoded = base64_encode(json_encode($policy));
$signature = base64_encode(hash_hmac('sha1', $policy_encoded, $config['secret_key'], true));
return [
'storage_type' => 'aliyun',
'upload_url' => "https://{$config['bucket']}.{$config['endpoint']}",
'credentials' => [
'OSSAccessKeyId' => $config['access_key'],
'policy' => $policy_encoded,
'signature' => $signature,
'key' => $file_path,
'success_action_status' => '200',
],
'file_path' => $file_path,
'domain' => $config['domain'] ?? "https://{$config['bucket']}.{$config['endpoint']}",
'max_size' => $max_size,
'expired' => $expired
];
}
/**
* 生成文件路径
* @param string $file_type 文件类型
* @param string $file_name 文件名
* @return string
*/
private function generateFilePath($file_type, $file_name = '')
{
$type_dir_map = [
'image' => 'upload/attachment/image',
'video' => 'upload/attachment/video',
'document' => 'upload/attachment/document'
];
$dir = $type_dir_map[$file_type] ?? 'upload/attachment/file';
$date_dir = date('Y/m/d');
if (empty($file_name)) {
$file_name = uniqid() . '_${filename}'; // ${filename} 将被前端替换
}
return $dir . '/' . $date_dir . '/' . $file_name;
}
/**
* 根据文件类型获取最大文件大小
* @param string $file_type 文件类型
* @return int 字节数,0表示不限制
*/
private function getMaxFileSize($file_type)
{
$size_map = [
'image' => 10 * 1024 * 1024, // 10MB
'video' => 500 * 1024 * 1024, // 500MB
'document' => 50 * 1024 * 1024, // 50MB
];
return $size_map[$file_type] ?? 10 * 1024 * 1024;
}
/**
* 确认上传完成
* @param array $data 文件信息
* @return array
*/
public function confirmUpload($data)
{
// 这里可以将文件信息保存到附件表,供后续管理使用
// 暂时直接返回文件URL,实际项目中可以根据需要保存到数据库
return [
'url' => $data['file_url'],
'name' => $data['file_name'],
'size' => $data['file_size'],
'type' => $data['file_type'],
'storage_type' => $data['storage_type']
];
}
}