




















<?php header('Content-Type: text/html; charset=UTF-8'); /** * 使用原生HTTP请求上传文件到OSS * (大文件分片上传) */ class AliyunOSSUploader { private $accessKeyId; private $accessKeySecret; private $bucket; private $endpoint; private $partSize = 5 * 1024 * 1024; // 5MB 分片大小 private $maxRetries = 3; public function __construct() { $this->accessKeyId = 'AccessKey ID'; $this->accessKeySecret = 'AccessKey Secret'; $this->bucket = 'Bucket名称'; $this->endpoint = 'OSS Endpoint'; } /** * 生成签名 */ private function sign($stringToSign) { return base64_encode(hash_hmac('sha1', $stringToSign, $this->accessKeySecret, true)); } /** * 生成请求头 */ private function generateHeaders($method, $resource, $headers = []) { $date = gmdate('D, d M Y H:i:s \G\M\T'); $contentType = isset($headers['Content-Type']) ? $headers['Content-Type'] : ''; $stringToSign = $method . "\n" . (isset($headers['Content-MD5']) ? $headers['Content-MD5'] : '') . "\n" . $contentType . "\n" . $date . "\n" . $resource; $signature = $this->sign($stringToSign); $defaultHeaders = [ 'Date' => $date, 'Authorization' => 'OSS ' . $this->accessKeyId . ':' . $signature, 'Content-Type' => $contentType ]; return array_merge($defaultHeaders, $headers); } /** * 初始化分片上传 */ public function initiateMultipartUpload($objectKey) { $resource = '/' . $this->bucket . '/' . $objectKey . '?uploads'; $url = 'http://' . $this->bucket . '.' . $this->endpoint . '/' . $objectKey . '?uploads'; $headers = $this->generateHeaders('POST', $resource, [ 'Content-Type' => 'application/octet-stream' ]); $ch = curl_init(); curl_setopt_array($ch, [ CURLOPT_URL => $url, CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true, CURLOPT_HTTPHEADER => $this->formatHeaders($headers), CURLOPT_HEADER => true ]); $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($httpCode == 200) { $xml = simplexml_load_string(substr($response, strpos($response, '<?xml'))); return (string)$xml->UploadId; } throw new Exception('初始化分片上传失败: ' . $response); } /** * 上传分片 */ public function uploadPart($objectKey, $uploadId, $partNumber, $filePath, $offset, $partSize) { $resource = '/' . $this->bucket . '/' . $objectKey . '?partNumber=' . $partNumber . '&uploadId=' . $uploadId; $url = 'http://' . $this->bucket . '.' . $this->endpoint . '/' . $objectKey . '?partNumber=' . $partNumber . '&uploadId=' . $uploadId; // 读取文件分片 $fileHandle = fopen($filePath, 'rb'); fseek($fileHandle, $offset); $partData = fread($fileHandle, $partSize); fclose($fileHandle); // 计算 MD5 $contentMD5 = base64_encode(md5($partData, true)); $headers = $this->generateHeaders('PUT', $resource, [ 'Content-Type' => 'application/octet-stream', 'Content-MD5' => $contentMD5, 'Content-Length' => strlen($partData) ]); // 重试机制 for ($retry = 0; $retry < $this->maxRetries; $retry++) { $ch = curl_init(); curl_setopt_array($ch, [ CURLOPT_URL => $url, CURLOPT_RETURNTRANSFER => true, CURLOPT_PUT => true, CURLOPT_HTTPHEADER => $this->formatHeaders($headers), CURLOPT_HEADER => true, CURLOPT_INFILE => fopen('data://application/octet-stream;base64,' . base64_encode($partData), 'r'), CURLOPT_INFILESIZE => strlen($partData) ]); $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); $headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE); curl_close($ch); if ($httpCode == 200) { $headers = substr($response, 0, $headerSize); preg_match('/ETag: "([^"]+)"/', $headers, $matches); return isset($matches[1]) ? $matches[1] : ''; } // 失败后等待重试 if ($retry < $this->maxRetries - 1) { usleep(pow(2, $retry) * 100000); // 指数退避 } } } /** * 格式化请求头 */ private function formatHeaders($headers) { $formatted = []; foreach ($headers as $key => $value) { $formatted[] = $key . ': ' . $value; } return $formatted; } /** * 完成分片上传 */ public function completeMultipartUpload($objectKey, $uploadId, $parts) { ksort($parts); $resource = '/' . $this->bucket . '/' . $objectKey . '?uploadId=' . $uploadId; $url = 'http://' . $this->bucket . '.' . $this->endpoint . '/' . $objectKey . '?uploadId=' . $uploadId; // 构建 XML 请求体 $xml = '<?xml version="1.0" encoding="UTF-8"?>'; $xml .= '<CompleteMultipartUpload>'; foreach ($parts as $partNumber => $etag) { $xml .= '<Part>'; $xml .= '<PartNumber>' . $partNumber . '</PartNumber>'; $xml .= '<ETag>' . $etag . '</ETag>'; $xml .= '</Part>'; } $xml .= '</CompleteMultipartUpload>'; $headers = $this->generateHeaders('POST', $resource, [ 'Content-Type' => 'application/xml', 'Content-Length' => strlen($xml) ]); $ch = curl_init(); curl_setopt_array($ch, [ CURLOPT_URL => $url, CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true, CURLOPT_HTTPHEADER => $this->formatHeaders($headers), CURLOPT_POSTFIELDS => $xml, CURLOPT_HEADER => false ]); $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($httpCode == 200) { return true; } throw new Exception('完成分片上传失败: ' . $response); } /** * 执行完整的分片上传 */ public function uploadLargeFile($filePath, $objectKey) { try { // 2. 计算分片数量 $fileSize = filesize($filePath); $partCount = ceil($fileSize / $this->partSize); $parts = []; $resumeFile = 'oss_upload_' . md5($filePath . $objectKey) . '.json'; $uploadState = []; if (file_exists($resumeFile)) { $uploadState = json_decode(file_get_contents($resumeFile), true); $uploadId = $uploadState['uploadId']; $completedParts = $uploadState['completedParts']; } else { // 初始化上传 $uploadId = $this->initiateMultipartUpload($objectKey); $completedParts = []; $uploadState = [ 'uploadId' => $uploadId, 'objectKey' => $objectKey, 'filePath' => $filePath, 'completedParts' => $completedParts ]; file_put_contents($resumeFile, json_encode($uploadState)); } // 3. 逐个上传分片 for ($i = 1; $i <= $partCount; $i++) { if (isset($completedParts[$i])) { $parts[$i] = $completedParts[$i]; continue; // 跳过已上传的分片 } $offset = ($i - 1) * $this->partSize; $currentPartSize = ($i == $partCount) ? ($fileSize - $offset) : $this->partSize; $etag = $this->uploadPart($objectKey, $uploadId, $i, $filePath, $offset, $currentPartSize); $parts[$i] = $etag; $uploadState['completedParts'][$i] = $etag; file_put_contents($resumeFile, json_encode($uploadState)); echo "上传分片 {$i}/{$partCount} 完成\n"; } // 4. 完成分片上传 $this->completeMultipartUpload($objectKey, $uploadId, $parts); // 清理进度文件 if (file_exists($resumeFile)) { unlink($resumeFile); } return true; } catch (Exception $e) { echo "上传失败: " . $e->getMessage() . "\n"; return false; } } /** * 列出已上传的分片 */ public function listParts($objectKey, $uploadId) { $resource = '/' . $this->bucket . '/' . $objectKey.'?uploadId='.$uploadId; $url = "http://{$this->bucket}.{$this->endpoint}/{$objectKey}?uploadId={$uploadId}"; $headers = $this->generateHeaders('GET', $resource,['Host' => $this->bucket . '.' . $this->endpoint]); $ch = curl_init(); curl_setopt_array($ch, [ CURLOPT_URL => $url, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => $this->formatHeaders($headers) ]); $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($httpCode == 200) { $xml = simplexml_load_string($response); $parts = []; foreach ($xml->Part as $part) { $parts[(int)$part->PartNumber] = (string)$part->ETag; } return $parts; } return []; } /** * 上传小文件 */ public function uploadFile($file_path,$file_type, $oss_path) { set_time_limit(0); $resource = '/' . $this->bucket . '/' . $oss_path; $url = 'http://' . $this->bucket . '.' . $this->endpoint . '/' . $oss_path; $file_size = filesize($file_path); // 构建请求头 $headers = $this->generateHeaders('PUT', $resource, [ 'Host' => $this->bucket . '.' . $this->endpoint, 'Content-Type' => $file_type, 'Content-Disposition' => 'inline', 'Content-Length' => $file_size ]); // 发送PUT请求 $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_PUT, true); curl_setopt($ch, CURLOPT_HTTPHEADER,$this->formatHeaders($headers)); curl_setopt($ch, CURLOPT_INFILE, fopen($file_path, 'r')); curl_setopt($ch, CURLOPT_INFILESIZE, $file_size); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); $response = curl_exec($ch); $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); return array('code'=>$http_code,'url'=>$url,'response'=>$response); } /** * 删除文件 */ public function deleteOssObject($objectKey) { $resource = '/' . $this->bucket . '/' . $objectKey; $url = "https://{$this->bucket}.{$this->endpoint}/" . ($objectKey); $headers = $this->generateHeaders('DELETE', $resource); // 初始化cURL $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "DELETE"); curl_setopt($ch, CURLOPT_HTTPHEADER, $this->formatHeaders($headers)); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); // 执行请求 $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); // 检查响应 if ($httpCode == 204) { return true; // 删除成功 } else { return "删除失败,HTTP状态码: {$httpCode},响应: {$response}"; } } } $uploader = new AliyunOSSUploader(); // 上传小文件 if($_FILES && $_FILES['video_file'] && $_FILES['video_file']['size']<100*1024*1024){ $upload_file = $_FILES['video_file']; $oss_filename = 'videos/' . date('Ymd') . '/' . uniqid() . '.' . pathinfo($upload_file['name'], PATHINFO_EXTENSION); $res = $uploader->uploadFile($upload_file['tmp_name'],$upload_file['type'], $oss_filename); print_r('<pre>'); print_r($res); print_r('</pre>'); exit(); } // 上传大文件 if($_FILES && $_FILES['video_file'] && $_FILES['video_file']['size']>100*1024*1024){ $upload_file = $_FILES['video_file']; $oss_filename = 'videos/' . date('Ymd') . '/' . uniqid() . '.' . pathinfo($upload_file['name'], PATHINFO_EXTENSION); print_r('<pre>'); print_r($upload_file['tmp_name']); print_r('</pre>'); print_r('<pre>'); print_r($oss_filename); print_r('</pre>'); exit(); $result = $uploader->uploadLargeFile($upload_file['tmp_name'], $oss_filename); // 5MB 分片 if ($result){ echo "文件上传成功!\n"; echo "文件地址: http://" . $config['bucket'] . '.' . $config['endpoint'] . '/' . $objectKey . "\n"; } else{ echo "文件上传失败!\n"; } } //删除文件 //$res = $uploader->deleteOssObject('videos/20251129/692a87ab21581.mp4'); // 创建上传实例 $uploader = new AliyunOSSUploader(); //列出已上传的分片 /*$res = $uploader->listParts('uploads/DE-GS99864.zip','F198E9334004453FA2E92E2C073ABB62'); print_r('<pre>'); print_r($res); print_r('</pre>'); exit();*/ // 上传大文件 $filePath = 'DE-GS99864.zip'; $objectKey = 'uploads/DE-GS99864.zip'; // OSS 中的文件路径 $result = $uploader->uploadLargeFile($filePath, $objectKey); // 5MB 分片 if ($result) { echo "文件上传成功!\n"; echo "文件地址: http://" . $config['bucket'] . '.' . $config['endpoint'] . '/' . $objectKey . "\n"; } else { echo "文件上传失败!\n"; } exit(); ?> <form method="post" enctype="multipart/form-data"> <input type="file" name="video_file" accept="video/*"> <input type="submit" value="上传视频"> </form>
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。