| @@ -2,8 +2,9 @@ | ||
| 2 | 2 | |
| 3 | 3 | namespace SyncBasalam\Services\Api; |
| 4 | 4 | |
| 5 | 5 | use SyncBasalam\Logger\Logger; |
| 6 | +use SyncBasalam\Services\MediaMimeType; | |
| 6 | 7 | |
| 7 | 8 | defined('ABSPATH') || exit; |
| 8 | 9 | |
| 9 | 10 | class FileUploadApiService |
| @@ -18,21 +19,99 @@ | ||
| 18 | 19 | 'error' => $fileValidation['message'] |
| 19 | 20 | ]; |
| 20 | 21 | } |
| 21 | 22 | |
| 23 | + if (function_exists('curl_init') && class_exists('CURLFile')) { | |
| 24 | + return $this->uploadWithCurl($url, $filePath, $data, $headers, $options); | |
| 25 | + } | |
| 26 | + | |
| 27 | + $fileSize = filesize($filePath); | |
| 28 | + if ($fileSize !== false && $fileSize > 10 * 1024 * 1024) { | |
| 29 | + return [ | |
| 30 | + 'body' => null, | |
| 31 | + 'status_code' => 500, | |
| 32 | + 'error' => 'برای آپلود فایلهای بزرگ، افزونه cURL در PHP باید فعال باشد.', | |
| 33 | + ]; | |
| 34 | + } | |
| 35 | + | |
| 22 | 36 | $boundary = wp_generate_password(24); |
| 23 | 37 | $payload = $this->makePayloadupload($data, $filePath, $boundary); |
| 24 | - | |
| 25 | 38 | $headers = array_merge($headers, ['content-type' => 'multipart/form-data; boundary=' . $boundary]); |
| 39 | + $timeout = isset($options['timeout']) ? (int) $options['timeout'] : 120; | |
| 26 | 40 | |
| 27 | - $response = wp_remote_post( | |
| 28 | - $url, | |
| 29 | - ['headers' => $headers, 'body' => $payload, 'timeout' => 10,], | |
| 30 | - ); | |
| 41 | + $response = wp_remote_post($url, [ | |
| 42 | + 'headers' => $headers, | |
| 43 | + 'body' => $payload, | |
| 44 | + 'timeout' => max(10, $timeout), | |
| 45 | + ]); | |
| 31 | 46 | |
| 32 | 47 | return $this->handleResponse($response, $url); |
| 33 | 48 | } |
| 34 | 49 | |
| 50 | + private function uploadWithCurl(string $url, string $filePath, array $data, array $headers, array $options): array | |
| 51 | + { | |
| 52 | + $mimeType = MediaMimeType::detect($filePath); | |
| 53 | + $fields = $data; | |
| 54 | + $fields['file'] = new \CURLFile($filePath, $mimeType, basename($filePath)); | |
| 55 | + $headerLines = []; | |
| 56 | + | |
| 57 | + foreach ($headers as $name => $value) { | |
| 58 | + if (strtolower((string) $name) === 'content-type') continue; | |
| 59 | + $headerLines[] = $name . ': ' . $value; | |
| 60 | + } | |
| 61 | + | |
| 62 | + $timeout = isset($options['timeout']) ? (int) $options['timeout'] : 120; | |
| 63 | + $curl = curl_init($url); | |
| 64 | + | |
| 65 | + curl_setopt_array($curl, [ | |
| 66 | + CURLOPT_POST => true, | |
| 67 | + CURLOPT_POSTFIELDS => $fields, | |
| 68 | + CURLOPT_HTTPHEADER => $headerLines, | |
| 69 | + CURLOPT_RETURNTRANSFER => true, | |
| 70 | + CURLOPT_CONNECTTIMEOUT => 20, | |
| 71 | + CURLOPT_TIMEOUT => max(10, $timeout), | |
| 72 | + CURLOPT_FOLLOWLOCATION => false, | |
| 73 | + CURLOPT_SSL_VERIFYPEER => true, | |
| 74 | + CURLOPT_SSL_VERIFYHOST => 2, | |
| 75 | + ]); | |
| 76 | + | |
| 77 | + $body = curl_exec($curl); | |
| 78 | + $statusCode = (int) curl_getinfo($curl, CURLINFO_HTTP_CODE); | |
| 79 | + | |
| 80 | + if ($body === false) { | |
| 81 | + $message = curl_error($curl); | |
| 82 | + curl_close($curl); | |
| 83 | + | |
| 84 | + Logger::error('خطا در ارسال فایل با cURL: ' . $message, ['url' => $url]); | |
| 85 | + | |
| 86 | + return [ | |
| 87 | + 'body' => null, | |
| 88 | + 'status_code' => 500, | |
| 89 | + 'error' => $message ?: 'خطای نامشخص در ارسال فایل', | |
| 90 | + ]; | |
| 91 | + } | |
| 92 | + | |
| 93 | + curl_close($curl); | |
| 94 | + RequestStatusTracker::recordHttpStatus($statusCode, $url); | |
| 95 | + $decodedBody = json_decode($body, true); | |
| 96 | + | |
| 97 | + if ($statusCode >= 400) { | |
| 98 | + $reason = RequestStatusTracker::describeHttpStatusFa($statusCode); | |
| 99 | + Logger::error('آپلود فایل به باسلام ناموفق بود. ' . $reason, [ | |
| 100 | + 'url' => $url, | |
| 101 | + 'status_code' => $statusCode, | |
| 102 | + 'reason' => $reason, | |
| 103 | + 'response_body' => $decodedBody ?? $body, | |
| 104 | + ]); | |
| 105 | + } | |
| 106 | + | |
| 107 | + return [ | |
| 108 | + 'body' => $decodedBody, | |
| 109 | + 'status_code' => $statusCode, | |
| 110 | + 'error' => $statusCode >= 400 ? $this->extractErrorMessage($decodedBody, $body) : null, | |
| 111 | + ]; | |
| 112 | + } | |
| 113 | + | |
| 35 | 114 | private function validateFile(string $filePath, array $options = []): array |
| 36 | 115 | { |
| 37 | 116 | if (!file_exists($filePath)) { |
| 38 | 117 | return ['valid' => false, 'message' => 'فایل در دسترس نیست' . $filePath]; |
| @@ -129,10 +208,9 @@ | ||
| 129 | 208 | Logger::error('خطا در خواندن فایل با استفاده از file_get_contents: ' . $localFile); |
| 130 | 209 | return ''; |
| 131 | 210 | } |
| 132 | 211 | |
| 133 | - $filetypeInfo = wp_check_filetype($localFile); | |
| 134 | - $mimeType = $filetypeInfo['type'] ?? 'application/octet-stream'; | |
| 212 | + $mimeType = MediaMimeType::detect($localFile); | |
| 135 | 213 | |
| 136 | 214 | $payload .= '--' . $boundary . $eol; |
| 137 | 215 | $payload .= 'Content-Disposition: form-data; name="file"; filename="' . $filename . '"' . $eol; |
| 138 | 216 | $payload .= 'Content-Type: ' . $mimeType . $eol; |