环境: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, // 2GB
forceIframeTransport: false,
multipart: false, // 禁用 multipart/form-data
add: 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转化为自己需要的语言应该不难,有不同意见的可以评论或私信交流