惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Latest news
Latest news
T
Troy Hunt's Blog
V
Vulnerabilities – Threatpost
L
LINUX DO - 热门话题
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Simon Willison's Weblog
Simon Willison's Weblog
V
V2EX
博客园 - 司徒正美
B
Blog RSS Feed
AWS News Blog
AWS News Blog
MyScale Blog
MyScale Blog
Scott Helme
Scott Helme
Cisco Talos Blog
Cisco Talos Blog
Last Week in AI
Last Week in AI
NISL@THU
NISL@THU
博客园 - Franky
P
Proofpoint News Feed
博客园_首页
C
CERT Recently Published Vulnerability Notes
雷峰网
雷峰网
S
Schneier on Security
P
Proofpoint News Feed
Hugging Face - Blog
Hugging Face - Blog
G
GRAHAM CLULEY
博客园 - 三生石上(FineUI控件)
月光博客
月光博客
WordPress大学
WordPress大学
The Hacker News
The Hacker News
T
Threatpost
阮一峰的网络日志
阮一峰的网络日志
A
Arctic Wolf
Microsoft Azure Blog
Microsoft Azure Blog
T
The Exploit Database - CXSecurity.com
Engineering at Meta
Engineering at Meta
罗磊的独立博客
T
The Blog of Author Tim Ferriss
D
Darknet – Hacking Tools, Hacker News & Cyber Security
I
Intezer
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
K
Kaspersky official blog
SecWiki News
SecWiki News
云风的 BLOG
云风的 BLOG
美团技术团队
C
Cybersecurity and Infrastructure Security Agency CISA
博客园 - 【当耐特】
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Security Latest
Security Latest
C
Cyber Attacks, Cyber Crime and Cyber Security
B
Blog
S
Security Affairs

博客园 - 天心PHP

去除文件夹下面所有xlsx的样式脚本 php 上传视频到阿里云OSS (不用SDK) SHEIN 开放平台授权 mercadolibre 美卡多 拉取listing Amazon SP API批量更新价格 JSON_LISTINGS_FEED Allegro API接口 Ozon API接口 Wildberries API接口 沃尔玛拉取listing和库存 沃尔玛修改促销价格 Amazon SP API 拉取产品详情和修改产品属性 Amazon SP API拉取分类,五点,描述和关键字 Amazon SP API拉取日期范围报告和分类树 PHP 提取PDF文件内容 常用sql(1.分组取最新的 N 条数据; 2.删除重复数据 ) kaufland平台api mercadolibre 美卡多 本土授权(PKCE) go mod Amazon SP API拉取listing 和 批量调价
php 上传视频到阿里云OSS (不用SDK) 升级版本 断点续传
天心PHP · 2025-12-01 · via 博客园 - 天心PHP
<?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>