This commit is contained in:
gaofeng
2026-05-12 18:27:28 +08:00
commit 6d9aee81aa
3664 changed files with 274415 additions and 0 deletions

View File

@@ -0,0 +1,126 @@
<?php
namespace app\service;
use Aws\S3\S3Client;
use think\facade\Log;
use think\File;
class AwsUploadService
{
/**
* 处理表单文件上传并转发到目标服务器
*
* @param string $fileField 表单文件字段名
* @param array $extraFields 额外传递的字段
* @return array 包含上传结果的数组
*/
public function uploadAndForward( File $file, string $fileField,array $extraFields = []): array
{
$mimeType = $file->getMime();
$s3config = config('filesystem.disks.s3');
$s3 = new S3Client($s3config);
try {
$config = config('app.upload_conf');
$configs=[];
foreach ($config as $key=>$con){
$configs[] = $key.':'.$con;
}
// 验证文件
$validate = validate([
$fileField =>implode('|',$configs),
]);
if (!$validate->check([$fileField => $file])) {
return $this->createErrorResult($validate->getError());
}
// 根据文件类型设置 Content-Type
if (in_array($mimeType, config('app.imageMimeTypes'))) {
$contentType = $mimeType; // 使用图片的实际 MIME 类型
} else {
$contentType = 'application/octet-stream'; // 非图片文件使用默认类型
}
$checkBucket = $this->checkBucket($s3, $extraFields['Bucket']);
if (!$checkBucket) {
return $this->createErrorResult('Bucket error');
}
$s3->putObject([
'Bucket' => $extraFields['Bucket'],
'Key' => $extraFields['Key'],
'Body' => fopen($file->getRealPath(), 'r'),
'ACL' => 'public-read',
'ContentType' => $contentType, // 设置检测到的 Content-Type
]);
return [
'code' => 1,
'data' =>[
'savename'=>$s3config['endpoint'].'/'.$extraFields['Bucket'].'/'.$extraFields['Key']
] ,
];
} catch (\Exception $e) {
return $this->createErrorResult('上传过程中发生异常: ' . $e->getMessage());
}
}
public function checkBucket($s3, $bucket)
{
try {
$buckets = $s3->listBuckets()->get('Buckets');
$buckets_arr = array_column($buckets, 'Name');
if (!in_array($bucket, $buckets_arr)) {
$s3->createBucket([
'Bucket' => $bucket,
]);
$policy = [
'Version' => '2012-10-17',
'Statement' => [
[
'Effect' => 'Allow',
'Principal' => ['AWS' => ['*']],
'Action' => [
's3:GetBucketLocation',
's3:ListBucket',
's3:ListBucketMultipartUploads',
],
'Resource' => "arn:aws:s3:::$bucket",
],
[
'Effect' => 'Allow',
'Principal' => ['AWS' => ['*']],
'Action' => [
's3:DeleteObject',
's3:GetObject',
's3:ListMultipartUploadParts',
's3:PutObject',
's3:AbortMultipartUpload',
],
'Resource' => "arn:aws:s3:::$bucket/*",
],
],
];
$s3->putBucketPolicy([
'Bucket' => $bucket,
'Policy' => json_encode($policy),
]);
}
return true;
} catch (\Exception $e) {
Log::info('checkBucket error:' . $e->getMessage());
return false;
}
}
/**
* 创建错误结果数组
*
* @param string $message 错误信息
* @return array 错误结果
*/
private function createErrorResult(string $message): array
{
return [
'code' => 0,
'error' => $message
];
}
}

View File

@@ -0,0 +1,189 @@
<?php
namespace app\service;
use Curl\Curl;
class BaiduOcrService
{
private $access_token;
private $access_token_url = 'https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials';
private $ocr_url='https://aip.baidubce.com/rest/2.0/ocr/v1/';
public function __construct()
{
$this->getAccessToken();
}
/**
*
*护照识别
* @param string $fileData 图片的二进制数据
* @return array
*/
function passport($fileData)
{
if ($fileData === false) {
return [
'code' => 0,
'msg' => '无法读取上传的文件',
];
}
if (empty($this->access_token)){
return [
'code' => 0,
'msg' => '无法识别',
];
}
$curl = new Curl();
$curl->setOpt(CURLOPT_SSL_VERIFYPEER, false);
$curl->setOpt(CURLOPT_SSL_VERIFYHOST, 0);
$curl->post($this->ocr_url.'passport'.'?access_token='.$this->access_token, [
'image' => base64_encode($fileData),
]);
if ($curl->error) {
return [
'code' => 0,
'msg' => $curl->errorMessage,
];
} else {
if (isset($curl->response->error_code)){
return [
'code' => 0,
'msg' => '解析失败',
];
}
$datas = json_decode(json_encode($curl->response), true);
return [
'code' => 1,
'data' => parsePassport($datas)
];
}
}
public function getAccessToken()
{
if (file_exists(BAIDUACCESSTOKENDIR.'baidu_access_token')){
$access_token = file_get_contents(BAIDUACCESSTOKENDIR.'baidu_access_token');
$access_token = json_decode($access_token,true);
if ($access_token['expires_time'] > time()){
$this->access_token = $access_token['access_token'];
return [
'code' => 1,
'msg' => "获取成功",
];
}
}
$this->access_token_url = $this->access_token_url.'&client_id='.config('app.baidu_ocr.appkey').'&client_secret='.config('app.baidu_ocr.appsecket');
$curl = new Curl();
$curl->setHeader('Content-Type', 'application/json');
$curl->post($this->access_token_url);
if ($curl->error) {
return [
'code' => 0,
'msg' => $curl->errorMessage,
];
}
//进行存储access_token
$access_token = $curl->response->access_token;
$this->access_token = $access_token;
file_put_contents(BAIDUACCESSTOKENDIR.'baidu_access_token',json_encode(
[
'access_token' => $access_token,
'expires_time' => time() + $curl->response->expires_in - 600,
]
));
return [
'code' => 1,
'msg' => "获取成功",
];
}
/**
*
*身份证识别
* @param string $fileData 图片的二进制数据
* @return array
*/
function identification($fileData)
{
if ($fileData === false) {
return [
'code' => 0,
'msg' => '无法读取上传的文件',
];
}
if (empty($this->access_token)){
return [
'code' => 0,
'msg' => '无法识别',
];
}
$curl = new Curl();
$curl->post($this->ocr_url.'idcard'.'?access_token='.$this->access_token, [
'id_card_side' => "front",
'image' => base64_encode($fileData),
]);
if ($curl->error) {
return [
'code' => 0,
'msg' => $curl->errorMessage,
];
} else {
if (isset($curl->response->error_code)){
return [
'code' => 0,
'msg' => '解析失败',
];
}
$datas = json_decode(json_encode($curl->response), true);
return [
'code' => 1,
'data' => parseIdentification($datas)
];
}
}
/**
*
*护照识别
* @param string $fileData 图片的二进制数据
* @return array 包含压缩后图片信息的数组
*/
function visa($fileData)
{
if ($fileData === false) {
return [
'code' => 0,
'msg' => '无法读取上传的文件',
];
}
if (empty($this->access_token)){
return [
'code' => 0,
'msg' => '无法识别',
];
}
$curl = new Curl();
$curl->post($this->ocr_url.'accurate_basic'.'?access_token='.$this->access_token, [
'image' => base64_encode($fileData),
'language_type'=>'ENG'
]);
if ($curl->error) {
return [
'code' => 0,
'msg' => $curl->errorMessage,
];
} else {
if (isset($curl->response->error_code)){
return [
'code' => 0,
'msg' => '解析失败',
];
}
$datas = json_decode(json_encode($curl->response), true);
return [
'code' => 1,
'data' => parseVisa($datas)
];
}
}
}

View File

@@ -0,0 +1,186 @@
<?php
namespace app\service;
/**
* 压缩二进制流图片
*/
class CompressImgService
{
/**
* 压缩通过表单上传的图片
*
* @param string $fileData 图片的二进制数据
* @param int $quality 压缩质量0-100仅适用于JPEG和WebP
* @param string|null $targetFormat 目标格式('jpeg', 'png', 'webp',默认使用原格式)
* @return array 包含压缩后图片信息的数组
*/
function compress($fileData, $quality = 80, $targetFormat = null)
{
// 读取上传的二进制数据
$imageData = file_get_contents($fileData);
if ($imageData === false) {
return [
'code' => 0,
'message' => '无法读取上传的文件',
];
}
// 获取图片信息
$imageInfo = getimagesizefromstring($imageData);
if (!$imageInfo) {
return [
'code' => 0,
'message' => '无法解析图片信息',
];
}
// 确定原始和目标格式
$originalMimeType = $imageInfo['mime'];
$originalExtension = image_type_to_extension($imageInfo[2], false);
if ($targetFormat === null) {
$targetFormat = $originalExtension;
}
// 创建图片资源
switch (strtolower($originalExtension)) {
case 'jpeg':
case 'jpg':
$image = imagecreatefromstring($imageData);
break;
case 'png':
$image = imagecreatefromstring($imageData);
break;
case 'webp':
$image = imagecreatefromstring($imageData);
break;
default:
return [
'code' => 0,
'message' => '不支持的图片格式',
];
}
if (!$image) {
return [
'code' => 0,
'message' => '无法创建图片资源',
];
}
// 压缩并转换图片
ob_start();
switch (strtolower($targetFormat)) {
case 'jpeg':
case 'jpg':
imagejpeg($image, null, $quality);
$compressedMimeType = 'image/jpeg';
break;
case 'png':
$pngQuality = 9 - floor($quality / 11);
imagepng($image, null, $pngQuality);
$compressedMimeType = 'image/png';
break;
case 'webp':
if (function_exists('imagewebp')) {
imagewebp($image, null, $quality);
$compressedMimeType = 'image/webp';
} else {
imagejpeg($image, null, $quality);
$compressedMimeType = 'image/jpeg';
}
break;
default:
imagedestroy($image);
ob_end_clean();
return [
'code' => 0,
'message' => "不支持的目标格式: {$targetFormat}",
];
}
$compressedData = ob_get_clean();
imagedestroy($image);
// 返回结果
return [
'code' => 1,
'compressed_data' => $compressedData,
'base64' => 'data:' . $compressedMimeType . ';base64,' . base64_encode($compressedData),
];
}
public function compressImage($filePath, $fileSize)
{
// 获取图片信息
$imageInfo = getimagesize($filePath);
if (!$imageInfo) {
return false;
}
$mime = $imageInfo['mime'];
// 根据MIME类型创建图像资源
switch ($mime) {
case 'image/jpeg':
$image = imagecreatefromjpeg($filePath);
break;
case 'image/png':
$image = imagecreatefrompng($filePath);
break;
case 'image/gif':
$image = imagecreatefromgif($filePath);
break;
default:
return false;
}
if (!$image) {
return false;
}
// 计算压缩质量(根据文件大小动态调整)
$originalQuality = 85; // 初始质量
$targetSize = 2.5 * 1024 * 1024; // 目标大小2.5MB
// 如果文件很大,降低初始质量
if ($fileSize > 5 * 1024 * 1024) {
$quality = 75;
} elseif ($fileSize > 10 * 1024 * 1024) {
$quality = 65;
} else {
$quality = $originalQuality;
}
// 创建临时文件
$tempFile = tempnam(sys_get_temp_dir(), 'compressed_');
// 保存压缩后的图片
switch ($mime) {
case 'image/jpeg':
imagejpeg($image, $tempFile, $quality);
break;
case 'image/png':
// PNG使用压缩级别0-9需要转换
$pngQuality = 9 - round(($quality / 100) * 9);
imagepng($image, $tempFile, $pngQuality);
break;
case 'image/gif':
imagegif($image, $tempFile);
break;
}
// 释放内存
imagedestroy($image);
// 检查压缩后文件大小如果仍然大于3MB继续压缩
$compressedSize = filesize($tempFile);
if ($compressedSize > 3 * 1024 * 1024) {
// 递归压缩直到满足要求
return $this->compressImage($tempFile, $compressedSize);
}
return $tempFile;
}
}

View File

@@ -0,0 +1,45 @@
<?php
namespace app\service;
use Swoole\Client;
class SwooleService
{
private static ?self $instance = null;
private Client $client;
private string $host = 'ossup.jzvisa.com';
private int $port = 19501;
private float $timeout = 0.5;
// 私有化构造函数
private function __construct()
{
$this->client = new Client(SWOOLE_SOCK_TCP);
if (!$this->client->connect($this->host, $this->port, $this->timeout)) {
throw new \RuntimeException("Cannot connect to Swoole Server {$this->host}:{$this->port}");
}
}
// 获取单例
public static function getInstance(): self
{
if (self::$instance === null) {
self::$instance = new self();
}
return self::$instance;
}
// 发送任务
public function sendTask(array $task): bool
{
$data = json_encode($task) . "\r\n";
return $this->client->send($data);
}
// 关闭连接
public function close(): void
{
$this->client->close();
self::$instance = null;
}
}

View File

@@ -0,0 +1,133 @@
<?php
namespace app\service;
use think\Exception;
class UploadService
{
/**
* 处理表单文件上传并转发到目标服务器
*
* @param string $fileField 表单文件字段名
* @param string $targetUrl 目标服务器URL
* @param array $extraFields 额外传递的字段
* @param array $headers 额外的请求头
* @return array 包含上传结果的数组
*/
public function uploadAndForward(string $fileField, string $targetUrl, array $extraFields = [], array $headers = []): array
{
// 获取上传的文件
$file = request()->file($fileField);
if (!$file) {
return $this->createErrorResult('未上传文件或上传失败');
}
try {
$config = config('app.upload_conf');
$configs=[];
foreach ($config as $key=>$con){
$configs[] = $key.':'.$con;
}
// 验证文件
$validate = validate([
$fileField =>implode('|',$configs),
]);
if (!$validate->check([$fileField => $file])) {
return $this->createErrorResult($validate->getError());
}
// 使用cURL转发到目标服务器
$result = $this->forwardToTargetServer($file, $targetUrl, $extraFields, $headers);
return $result;
} catch (\Exception $e) {
return $this->createErrorResult('上传过程中发生异常: ' . $e->getMessage());
}
}
/**
* 使用cURL将文件转发到目标服务器
*
* @param \think\File $file 文件对象
* @param string $targetUrl 目标URL
* @param array $extraFields 额外字段
* @param array $headers 请求头
* @return array 包含响应信息的数组
*/
private function forwardToTargetServer(\think\File $file, string $targetUrl, array $extraFields = [], array $headers = []): array
{
$ch = curl_init();
try {
// 设置基本选项
curl_setopt($ch, CURLOPT_URL, $targetUrl);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HEADER, false);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 300); // 5分钟超时
// 构建上传数据
$postData = [
'file' => new \CURLFile(
$file->getPathname(),
$file->getOriginalMime(),
$file->getOriginalName()
)
];
// 添加额外字段
$postData = array_merge($postData, $extraFields);
curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
// 设置请求头
$httpHeaders = [];
foreach ($headers as $headerName => $headerValue) {
$httpHeaders[] = "$headerName: $headerValue";
}
if (!empty($httpHeaders)) {
curl_setopt($ch, CURLOPT_HTTPHEADER, $httpHeaders);
}
// 执行请求
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$curlInfo = curl_getinfo($ch);
// 检查错误
if ($response === false) {
throw new Exception('cURL错误: ' . curl_error($ch));
}
return [
'code' => 1,
'http_code' => $httpCode,
'response' => $response,
];
} catch (\Exception $e) {
return [
'code' => 0,
'error' => $e->getMessage()
];
} finally {
curl_close($ch);
}
}
/**
* 创建错误结果数组
*
* @param string $message 错误信息
* @return array 错误结果
*/
private function createErrorResult(string $message): array
{
return [
'code' => 0,
'error' => $message
];
}
}