PHP 华为云H5上传文件:临时链接上传文件和POST表单直传
环境:PHP7.3.4 CI框架
一、完成的大概步骤:
前置工作:(1)安装集成(2)配置好华为云的桶策略、桶ACL、CORS规则【测试期间可以全部用最高权限】
1、后端利用密钥生成临时链接或者表单直传的参数
2、将后端生成的参数传递到H5前端
3、前端根据后端生成的参数或链接直接上传就好。
二、详细步骤:
(1)利用composer或者手动安装华为云插件
composer require huaweicloud/huaweicloud-sdk-php:3.0.3-beta
(2)生成临时链接URL:
官方文档链接:1、临时授权访问OBS_典型场景配置案例_权限配置指南_对象存储服务 OBS-华为云2、使用临时安全凭证直传OBS_移动应用直传_数据直传OBS_最佳实践_对象存储服务 OBS-华为云
代码:Huawei.php
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
//根据自己的实际路径引入
require_once APPPATH . 'libraries/third_party/HuaweiCos/vendor/autoload.php';class Huawei {private $obsClient;private $bucketName;private $CI;private $cos_config;public function __construct() {$this->CI =& get_instance();//读入华为云的配置$this->CI->load->config('qcloud');$this->cos_config=$cos_config = $this->CI->config->item('qcloud');$this->bucketName = $cos_config['bucket'];$ak = $cos_config['secretId'];$sk = $cos_config['secretKey'];$endpoint = $cos_config['endpoint'];// 初始化 OBS 客户端$this->obsClient = \Obs\ObsClient::factory(['key' => $ak,'secret' => $sk,'endpoint' => $endpoint,'socket_timeout' => 30,'connect_timeout' => 10]);}// 获取临时链接public function getFederationToken($path) { $objectKey = $path;try {$res = $this->obsClient->createSignedUrl(['Method' => 'PUT','Bucket' => $this->bucketName,'Key' => $objectKey,'Expires' => 3600,'Headers' => ['Content-Type' => 'application/octet-stream']]);return ['success' => true,'domain'=>$this->cos_config['domain'],'signed_url' => $res['SignedUrl'],'object_key' => $objectKey];} catch (Exception $e) {log_message('error', 'Failed to generate signed URL: ' . $e->getMessage());return ['success' => false, 'error' => 'Failed to generate signed URL: ' . $e->getMessage()];}}// 临时获取post请求相关参数public function getFederationTokenPost($path) {$objectKey = $path;try {$formParams = [// Set object access permission to public-read'x-obs-acl' => ObsClient::AclPublicRead,// Set object MIME type'content-type' => 'text/plain'];// Set expiration time for the signed request (in seconds)$expires = 3600;// Generate POST signature$res = $this->obsClient->createPostSignatureSync(['Expires' => $expires,'FormParams' => $formParams]);return ['success' => true,'domain'=>$this->cos_config['domain'],'signed_url' => $res['SignedUrl'],'object_key' => $objectKey];}catch (Exception $e) {log_message('error', 'Failed to generate signed URL: ' . $e->getMessage());return ['success' => false, 'error' => 'Failed to generate signed URL: ' . $e->getMessage()];}}
}
(3)获取临时链接并发送给前端
Test.php
<?php
class Test extends CI_Controller
{public function index(){$this->load->library('cdnupload/CdnFactory');$cdn=CdnFactory::getCdn_foster('Huawei');// 存储的相对链接 $uploadurl='Test/android/test/abc.jpg';$filename='abc.jpg';// put获取临时签名$res=$cdn->getFederationToken($uploadurl,$filename);$data['filename']=$filename;$data['signed_url']=$res['signed_url'];$data['object_key']=$res['object_key'];$data['domain']=$res['domain'];$data['UploadPath']=$uploadurl;$data['new_version'] = '1.0'; $this->load->view("test",$data);}
}
(4)前端执行直传操作
test.html
<!DOCTYPE html>
<html>
<head><title>H5 File Upload to Huawei Cloud OBS</title><link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/blueimp-file-upload/10.32.0/css/jquery.fileupload.css"><link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/blueimp-file-upload/10.32.0/css/jquery.fileupload-ui.css"><link href="/statics/css/bootstrap.min.css" rel="stylesheet" crossorigin="anonymous"><style>.progress-bar { transition: width 0.3s ease-in-out; }.upload-container { max-width: 800px; margin: 50px auto; }.fileupload-buttonbar { margin-bottom: 20px; }.files tr td { vertical-align: middle; }.progress-cell { width: 200px; }.size-cell { width: 100px; }.operation-cell { width: 150px; }.retry-btn { margin-left: 10px; }.status-alert { margin-top: 20px; }.upload-speed { font-size: 12px; color: #666; }.table-striped tbody tr:hover { background-color: #f8f9fa; }</style>
</head>
<body><div class="container upload-container"><h3 class="text-center"><?php echo htmlspecialchars($game_info['name'], ENT_QUOTES, 'UTF-8'); ?>母包上传</h3><form id="fileupload" method="PUT" ><div class="fileupload-buttonbar"><div class="fileupload-buttons"><span class="fileinput-button btn btn-primary"><span>选择文件</span><input type="file" name="files[]" accept=".ipa,.apk"></span><button type="submit" class="btn btn-success start" disabled><span>开始上传</span></button></div></div><table role="presentation" class="table table-striped"><thead><tr><th>游戏名</th><th>版本号</th><th>文件名</th><th>文件大小</th><th>上传进度</th><th>操作</th></tr></thead><tbody class="files"></tbody></table></form><div id="status" class="alert status-alert" style="display:none;"></div></div><script src="https://code.jquery.com/jquery-3.6.0.min.js"></script><script src="/assets/libs/fastadmin-layer/dist/layer.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/blueimp-file-upload/10.32.0/js/vendor/jquery.ui.widget.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/blueimp-file-upload/10.32.0/js/jquery.iframe-transport.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/blueimp-file-upload/10.32.0/js/jquery.fileupload.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/blueimp-file-upload/10.32.0/js/jquery.fileupload-process.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/blueimp-file-upload/10.32.0/js/jquery.fileupload-validate.js"></script><script>$(function () {'use strict';var cdnDomain = 'http://<?php echo htmlspecialchars($domain, ENT_QUOTES, 'UTF-8'); ?>';var filenameURL="<?=$filename?>";var signedUrl = '<?php echo htmlspecialchars($signed_url, ENT_QUOTES, 'UTF-8'); ?>';var objectKey = '<?php echo htmlspecialchars($object_key, ENT_QUOTES, 'UTF-8'); ?>';var isUploading = false;if (!signedUrl || !objectKey) {layer.msg('错误:无效的签名 URL 或对象键', {icon: 2, time: 2000});$('#status').addClass('alert-danger').text('错误:无效的签名 URL 或对象键。').show();$('.start').prop('disabled', true);return;}$('#fileupload').fileupload({url: signedUrl,type: 'PUT',dataType: 'xml',acceptFileTypes: /(\.|\/)(ipa|apk)$/i,maxFileSize: 2 * 1024 * 1024 * 1024, // 2GBforceIframeTransport: false,multipart: false, // 禁用 multipart/form-dataadd: function (e, data) {if (isUploading) {layer.msg('正在上传,请勿重复操作', {icon: 2, time: 2000});return;}var file = data.files[0];var ext = file.name.substring(file.name.lastIndexOf('.') + 1).toLowerCase();var expectedExt = objectKey.substring(objectKey.lastIndexOf('.') + 1).toLowerCase();$('.files').empty();data.context = $('<tr/>').appendTo('.files');$('<td/>').text(objectKey.split('/').pop()).appendTo(data.context);$('<td/>').addClass('size-cell').text((file.size / 1024 / 1024).toFixed(2) + ' MB').appendTo(data.context);$('<td/>').addClass('progress-cell').append($('<div/>').addClass('progress').append($('<div/>').addClass('progress-bar progress-bar-success').attr('role', 'progressbar').css('width', '0%')).append( )).appendTo(data.context);$('<td/>').addClass('operation-cell').append($('<button/>').addClass('btn btn-sm btn-warning retry-btn').text('重试').hide().click(function () {if (!isUploading) {data.context.find('.progress-bar').removeClass('progress-bar-danger').css('width', '0%').text('0%'); $('#status').hide();data.submit();}})).appendTo(data.context);$('.start').prop('disabled', false);data.context.data('fileData', data);},beforeSend: function (xhr, data) {var file = data.files[0];var contentType = 'application/octet-stream';xhr.setRequestHeader('Content-Type', contentType);data.startTime = new Date().getTime();isUploading = true;$('.start').prop('disabled', true);},progress: function (e, data) {var progress = parseInt(data.loaded / data.total * 100, 10) || 0;data.context.find('.progress-bar').css('width', progress + '%').text(progress + '%');var elapsed = (new Date().getTime() - (data.startTime || new Date().getTime())) / 1000;var speed = 0;if (elapsed > 0.1 && data.loaded && data.total) {speed = data.loaded / elapsed / 1024;}// data.context.find('.upload-speed').text(speed.toFixed(2) + ' KB/s');},done: function (e, data) {isUploading = false;layer.msg('上传成功', {icon: 1, time: 2000});updateVersionCallback(data.context);var cdnUrl = cdnDomain + '/' + objectKey;data.context.find('.progress-cell .progress-bar').addClass('progress-bar-success').text('已完成');data.context.find('.operation-cell').html($('<a/>').addClass('btn btn-sm btn-primary').attr('href', cdnUrl).attr('target', '_blank').text('下载'));$('.start').prop('disabled', true);},fail: function (e, data) {isUploading = false;var errorMsg = '上传失败:' + (data.jqXHR && data.jqXHR.responseXML ? $(data.jqXHR.responseXML).find('Message').text() : '未知错误');layer.msg(errorMsg, {icon: 2, time: 3000});$('#status').removeClass('alert-success').addClass('alert-danger').text(errorMsg).show();data.context.find('.progress-cell .progress-bar').addClass('progress-bar-danger').text('失败');data.context.find('.retry-btn').show();$('.start').prop('disabled', false);}});$('.start').on('click', function (e) {if (isUploading) {layer.msg('正在上传,请勿重复点击', {icon: 2, time: 2000});return;}$('#status').html('<span style="color:red">上传处理过程中,请勿关闭页面</span>').show();e.preventDefault();$('.files tr').each(function () {var data = $(this).data('fileData');if (data) {data.submit();}});});function updateVersionCallback(context) {// 上传完成后的操作}});</script>
</body>
</html>
post上传:上述Huawei.php中的getFederationTokenPost就是获取post直传的相关参数
post.html
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body><form action="http://bucketname.your-endpoint/" method="post" enctype="multipart/form-data">
Object key
<!-- 对象名 -->
<input type="text" name="key" value="objectname" />
<p>
ACL
<!-- 对象ACL权限 -->
<input type="text" name="x-obs-acl" value="public-read" />
<p>
Content-Type
<!-- 对象MIME类型 -->
<input type="text" name="content-type" value="text/plain" />
<p>
<!-- policy的base64编码值 -->
<input type="hidden" name="policy" value="*** Provide your policy ***" />
<!-- AK -->
<input type="hidden" name="AccessKeyId" value="*** Provide your access key ***"/>
<!-- 签名串信息 -->
<input type="hidden" name="signature" value="*** Provide your signature ***"/><input name="file" type="file" />
<input name="submit" value="Upload" type="submit" />
</form>
</body>
</html>
post直传的相关文档:
1、Web端通过PostObject接口直传OBS_数据直传OBS_最佳实践_对象存储服务 OBS-华为云
里面的代码用AI转化为自己需要的语言应该不难,有不同意见的可以评论或私信交流