零、准备

要是实现将文件上传到阿里云OSS,首先就要开通了OSS服务,然后创建bucket之类的。这些就不多说了。

稍微看了下文档就能看见有accessKeyId,accessKeySecret,endpoint,bucket等之类的。这些要哪里来呢。

  1. accessKeyId,accessKeySecret

    在控制台找到RAM访问控制它大概长下面这样,在这里创建用户,就能得到accessKeyId,accessKeySecret了。注意:accessKeySecret只会显示一次,你创建的时候就要保存起来,要不然就要重创了。

  2. 然后就要给创建的用户权限,然后才能操作OSSAPI。

  3. 然后就是endpoint,bucket这些了,要在'对象存储 OSS'页面中拿。

    Bucket列表能看到Bucket的名称,在每个bucket的概览里就能看到endpoint之类的了。

结合PHP(或者纯前端)主要介绍三种不同的向OSS上传文件的方式,都是用过或者踩过坑的。

一、服务端签名后直传

优点:官方文档

  • 使用插件(如,element upload, web uploader等)上传,不用重写上传方法,通过地址提交,可以方便的使用插件提供的功能。
  • 前端值传不进过应用服务器中传,效率更高,而且前端也能方便获取上传进度。
  • 需要后端进行签名,前端获取签名后再去上传,也不会暴露accessKeySecret,accessKeyId等oss信息。

1. 阿里云控制台配置

创建跨越规则,否则web前端上传的时候会报CORS跨越错误。** 具体操作如下图:

2. 后端接口开发(PHP)

可以参考下官方示例代码PHP),然后我们按照自己需要进行修改就基本上能实现了。这里是修改后的代码,因为只需将视频上传到OSS中,而且上传后的地址也是知道的,所以就注释了上传回调相关的操作,代码如下:

use DateTime;

class ContentsController extends Controller {
// 阿里云oss配置
private $accessKeyId = 'xxxxxxxx'; // 请填写您的AccessKeyId
private $accessKeySecret = 'xxxxxx'; // 请填写您的AccessKeySecret
private $bucket= "your-bucketName";
private $endpoint = "oss-cn-hangzhou.aliyuncs.com"; public function gmt_iso8601($time)
{
$dtStr = date("c", $time);
$mydatetime = new DateTime($dtStr);
$expiration = $mydatetime->format(DateTime::ISO8601);
$pos = strpos($expiration, '+');
$expiration = substr($expiration, 0, $pos);
return $expiration . "Z";
} // 阿里云签名接口
// 调用该接口可传入要上传到的文件夹dir
public function signature() {
{ $id = $this -> accessKeyId; // 请填写您的AccessKeyId。
$key = $this -> accessKeySecret; // 请填写您的AccessKeySecret。
$host = 'https://'.$this -> bucket.'.'.$this -> endpoint;
// $host = 'https://xxxx.oss-cn-hangzhou.aliyuncs.com'; // $host的格式为 bucketname.endpoint, $dir = trim(I('dir', '', 'string')); // 用户上传文件时指定的前缀。 // 若需要配置回调服务器URL 则此配置
// private $callbackUrl = 'http://你的域名/callback.php';//上传回调的地址 还记得上图中callback.php文件吗,把这文件放在你的项目中,配个路由保证能访问到这个文件就行,这个值就是访问callback.php此文件的URL 例如:我放在项目根目录 那值就为 http://liutong.pro/callback.php
// $callbackUrl = $this->callbackUrl;
// $callbackUrl为上传回调服务器的URL,请将下面的IP和Port配置为您自己的真实URL信息。
// $callback_param = array('callbackUrl' => $callbackUrl,
// 'callbackBody' => 'filename=${object}&size=${size}&mimeType=${mimeType}&height=${imageInfo.height}&width=${imageInfo.width}',
// 'callbackBodyType' => "application/x-www-form-urlencoded");
// $callback_string = json_encode($callback_param);
// $base64_callback_body = base64_encode($callback_string); $now = time();
$expire = 30; //设置该policy超时时间是10s. 即这个policy过了这个有效时间,将不能访问。
$end = $now + $expire;
$expiration = $this->gmt_iso8601($end); //最大文件大小.用户可以自己设置
$condition = array(0 => 'content-length-range', 1 => 0, 2 => 2147483648);
$conditions[] = $condition; // 表示用户上传的数据,必须是以$dir开始,不然上传会失败,这一步不是必须项,只是为了安全起见,防止用户通过policy上传到别人的目录。
$start = array(0 => 'starts-with', 1 => '$key', 2 => $dir);
$conditions[] = $start; $arr = array('expiration' => $expiration, 'conditions' => $conditions);
$policy = json_encode($arr);
$base64_policy = base64_encode($policy);
$string_to_sign = $base64_policy;
$signature = base64_encode(hash_hmac('sha1', $string_to_sign, $key, true)); $response = array();
$response['accessid'] = $id;
$response['host'] = $host;
$response['policy'] = $base64_policy;
$response['signature'] = $signature;
$response['expire'] = $end;
$response['callback'] = $base64_callback_body;
$response['dir'] = $dir; // 这个参数是设置用户上传文件时指定的前缀。
echo json_encode($response);
}
}
}

Java签名方法https://www.cnblogs.com/applesnt/p/14863057.html

3. 前端获取签名后上传

前端先调用签名接口(如上是'/syscenter/contents/signature'),返回签名信息:

{
"accessid": "xxxxxtAq4LFttzpLZqvn2N5Y",
"callback": null,
"dir": "upload/",
"expire": 1666918519,
"host": "https://zzxxx.oss-cn-hangzhou.aliyuncs.com",
"policy": "eyJxxxx0aW9uIjoiMjAyMi0xMC0yOFQwODo1NToxOVoiLCJjb25kaXRpb25zIjpbWyJjb250ZW50LWxlbmd0aC1yYW5nZSIsMCwyMTQ3NDgzNjQ4XSxbInN0YXJ0cy13aXRoIiwixxxxxxxxxx",
"signature": "Q4xxWOjB3kjGaNRF097LyG0SQ="
}

然后就可以使用签名信息进行上传了,提交到host,其他参数也一起带上,主要代码如下:

<template>
<el-upload
v-model:file-list="fileList"
:action="uploadUrl"
:data="uploadData"
>
<el-button type="primary">上传</el-button>
</el-upload>
</template> <script setup>
const uploadUrl = ref();
const uploadData = reactive() const getSignature = async () => {
const data = await axios.get('/syscenter/contents/signature', param: {dir:'/upload'})
uploadUrl.value = data.host
uploadData.policy = data.policy
uploadData.OSSAccessKeyId = data.accessid
uploadData['success_action_status'] = 200
uploadData.signature = data.signature
}
</script>

二、使用STS临时凭证进行上传

优点:官方文档

  1. 前端使用的是临时凭证访问的OSS,无需暴露长期密钥,更安全。
  2. 配合ali-oss能方便进行大文件的分片上传。

1. 后端接口开发(node)

const { STS } = require('ali-oss');
import express from "express";
const router = express.Router(); router.get("/getToken", function (req, res) {
let sts = new STS({
accessKeyId: "xxxxxxx",
accessKeySecret: "xxxx",
});
// assumeRole配置RAM用户的ARN。
// policy填写自定义权限策略,用于进一步限制STS临时访问凭证的权限。如果不指定Policy,则返回的STS临时访问凭证默认拥有指定角色的所有权限。
// expiration用于设置临时访问凭证有效时间单位为秒,最小值为900,最大值以当前角色设定的最大会话时间为准。本示例指定有效时间为3000秒。
// sessionName用于自定义角色会话名称,用来区分不同的令牌,例如填写为sessiontest。
sts
.assumeRole("acs:ram::175708322470****:role/ramtest", ``, "3000", "sessiontest")
.then((result) => {
console.log(result);
res.set("Access-Control-Allow-Origin", "*");
res.set("Access-Control-Allow-METHOD", "GET");
res.json({
AccessKeyId: result.credentials.AccessKeyId,
AccessKeySecret: result.credentials.AccessKeySecret,
SecurityToken: result.credentials.SecurityToken,
Expiration: result.credentials.Expiration,
});
})
.catch((err) => {
console.log(err);
res.status(400).json(err.message);
});
});
export default router;

2. 前端获取临时token再通过sdk进行上传

{
"accessKeyId": "STS.NSmigxxevTxbauZ5hMdM",
"accessKeySecret": "5wLHcDpqjojqZaVXFgDnBej7WHrao5Xzy6eGXufWzkSC",
"securityToken": "CAIS5QF1q6Ft5B2yfSjIr5DYIt3av7pX47i/Rx/homI0edUZh4jPrzz2IH5NfXhvBeAWtf83mmlY7/cdlqp0UIQd0O",
"region": "cn-hangzhou",
"bucket": "xxx-xx.oss-cn-hangzhou.aliyuncs.com",
"endpoint": "sts.cn-hangzhou.aliyuncs.com"
}

引用https://gosspublic.alicdn.com/aliyun-oss-sdk-6.17.0.min.js ,用了构建工具则npm i ali-oss使用其sdk,其仓库及文档地址,其中要注意的是,浏览器环境并不是所有的api都能使用的,很多方法调用后undefined就是用不了了:

  1. bucket相关的方法(如:listBuckets, putBucketLogging等)是用不了的。
  2. policy相关,calculatePostSignature等方法也用不了(6.17.0),所以想通过签名去上传也是行的。
  3. 是用sdk上传,其put(name, file[, options]),multipartUpload(name, file[, options])等方法都是通过put上传的,没有在控制台跨域配置勾选put则还是会报cors错误。
  4. 若使用multipartUpload上传,则一定条件下会自动分片,所以需在跨域配置中配置暴露header:ETag,否则也会报错。

使用element upload上传示例,可以同步显示上传进度

<template>
<el-upload
ref="tookitUploader"
action=""
:http-request="uploadFunction"
:before-upload="beforeUpload"
:on-exceed="OnExceed"
name="file"
:limit="1"
>
<el-button size="small">点击上传</el-button>
<div slot="tip" class="el-upload__tip">支持扩展名:.rar .zip .exe</div>
</el-upload>
</template> <script>
// 初始化OSS
initOSS() {
getOSSToken().then(async (res) => {
const data = res.data
const client = new OSS({
region: 'oss-cn-hangzhou',
accessKeyId: data.accessKeyId,
accessKeySecret: data.accessKeySecret,
stsToken: data.securityToken,
bucket: 'xxx',
refreshSTSToken: async () => {
const data = getOSSToken()
return {
accessKeyId: data.accessKeyId,
accessKeySecret: data.accessKeySecret,
stsToken: data.securityToken
}
},
refreshSTSTokenInterval: 300000
}) this.oss = client
})
}, // 重新的上传方法
uploadFunction(item) {
this.oss.multipartUpload('/xxxy/' + item.file.name, item.file, {
// 同步上传进度
// http-request返回的参数中有用onProgress可以同步进度
progress: function (p, checkpoint) {
item.onProgress({ percent: Math.floor(p * 100) });
}
}).then((res) => {
let url = res.res.requestUrls[0].split('?').pop();
this.uploadForm.filePath = url
this.uploadForm.attachList.push(url)
}).catch(() => {
this.$message.error('上传出错')
})
},
</script>

三、前端签名,使用PostObject直接上传(可配合使用STS)

1.应用场景

适合使用第三方的上传插件,可通过URL直接传入到OSS,同时能使用插件的自带的功能,而不用重写其内部方法。

生成签名的方法:

import '@/utils/crypto1/crypto/crypto'
import '@/utils/crypto1/hmac/hmac'
import '@/utils/crypto1/sha1/sha1'
import { Base64 } from 'js-base64' /**
* 生成policy和签名
* 默认1小时后失效
* @param {*} accessKeySecret oss accessKeySecret
* @param {*} expiration 过期时间 单位秒
* @returns
*/
export const genSignature = (accessKeySecret, expir = 600) => { const expiration = (new Date(Date.now() + expir * 1000)).toISOString()
const policy = {
expiration: expiration, // 失效时间
conditions: [
['content-length-range', 0, 469824028] // 设置上传文件的大小限制 1G
]
}
const policyBase64 = Base64.encode(JSON.stringify(policy)) const bytes = Crypto.HMAC(Crypto.SHA1, policyBase64, accessKeySecret, { asBytes: true });
const signature = Crypto.util.bytesToBase64(bytes); return {
policy:policyBase64,
signature
}
}

使用该方法可以在前端生成policy,signature,然后作为参数传递给url

如在el-upload中使用:

<el-upload action="http://xxxx.oss-cn-hangzhou.aliyuncs.com/xxxx/test/" :data="uploadData"></el-upload>

<script>
...
// 根据调用后端sts接口获取accessKeySecret,生成policy,signature
getOSSPOSTData() {
getOSSToken().then(async (res) => {
const data = res.data const signatureInfo = genSignature(data.accessKeySecret)
this.uploadData.OSSAccessKeyId = data.accessKeyId
this.uploadData.accessKeySecret = data.accessKeySecret
this.uploadData.policy = signatureInfo.policy
this.uploadData.Signature = signatureInfo.signature
this.uploadData['success_action_status'] = 200
this.uploadData['x-oss-security-token'] = data.securityToken
})
},
</script>

PostObject文档: PostObject

crypto1文件: crypto1

注:crypto1如用不是阿里云提供的可能会计算出来的hmac不一致导致,签名失败。

四、直接在前端使用sdk进行上传

当然我们还可以直接就在前端页面中,使用阿里云提供的js的sdk就能直接进行上传了,不过如此就会把OSS相关的AccessKeyId,AccessKeySecret之类的暴露出来就是。避免方法就是按照官方的建议搭建STS服务并使用其返回的临时访问凭证访问OSS。这里就没有进行尝试了,详细可参考官方文档

下面是没有使用STS,直接在前端操作的相关代码(使用的VUE和element-UI):

点击查看代码
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>文件上传</title>
<link
rel="stylesheet"
href="https://unpkg.com/element-ui/lib/theme-chalk/index.css"
/>
<script src="https://gosspublic.alicdn.com/aliyun-oss-sdk-6.17.0.min.js"></script>
</head> <body>
<div id="app">
<el-button @click="visible = true">Button</el-button>
<el-dialog :visible.sync="visible" title="文件上传">
<el-upload
class="upload-demo"
action=""
:http-request="upload"
name="file"
:on-preview="handlePreview"
:on-remove="handleRemove"
:before-remove="beforeRemove"
multiple
:limit="3"
:on-exceed="handleExceed"
:file-list="fileList"
>
<el-button size="small" type="primary">点击上传</el-button>
<div slot="tip" class="el-upload__tip">想传啥就传啥</div>
</el-upload>
</el-dialog>
</div>
</body>
<script src="https://unpkg.com/vue@2/dist/vue.js"></script>
<script src="https://unpkg.com/element-ui/lib/index.js"></script> <script>
new Vue({
el: "#app",
data: function () {
return {
visible: false,
oss: "",
fileList: [],
};
},
created() {
this.initOSS();
},
methods: {
initOSS() {
this.oss = new OSS({
// 阿里云oss相关信息
region: "ss-cn-hangzhou",
accessKeyId: "LTAIxxxxxwxxn7SM",
accessKeySecret: "zxxxxxxx6xxx8V",
bucket: "zflxx",
});
},
upload(item) {
this.oss.multipartUpload(item.filename, item.file).then((res) => {
console.log(res);
});
},
},
});
</script>
</html>

五、中转上传到应用服务器再上传到阿里云OSS

因为使用的是php,所以这里用的是OSS PHP SDK,可以参考官方文档安装好SDK后,然后就能可以调用其SDK开始操作了。

// 使用上传到OSS
public function uploadoss() {
$accessKeyId = "xxxcxvxve232"; ;
$accessKeySecret = "xxxxxxx12121212";
$bucket= "xxx";
$endpoint = "oss-cn-beijing.aliyuncs.com"; $upname = trim(I('upname', '', 'string'));
if (!$upname) {
$upname = trim(I('get.upname', '', 'string'));
} if (!empty($_FILES[$upname]['name'])) {
try { // 开启OSSClient
$ossClient = new OssClient($accessKeyId, $accessKeySecret, $endpoint);
} catch (OssException $e) {
print $e -> getMessage();
} $content = $_FILES[$upname];
$info = pathinfo($content["name"]);
$ext = $info['extension'];
$file_name = date("YmdHis").time().mt_rand(100000, 999999). ".{$ext}";
$object = "upload/".$file_name; try {
$result = $ossClient -> uploadFile($bucket, $object, $content['tmp_name']);
$ossUrl = $result['oss-request-url']; } catch (OssException $e) {
echo $e;
}
echo json_encode(array('flag' => 1, 'msg' => 'OK', 'picUrl' => $picUrl));
} else {
echo json_encode(array('flag' => -1, 'msg' => 'No', 'picUrl' => ''));
}
}

六、错误

  1. Please set the etag of expose-headers in OSS:https://help.aliyun.com/document_detail/277773.html

  2. CORS 错误:https://help.aliyun.com/document_detail/216832.html

    注使用(ali-oss)包put文件使用的是put方法,光勾选post还会报错。

  3. 使用element upload 与 ali-oss sdk时进行度获取

    https://github.com/ElemeFE/element/issues/9759

七、小结

  1. 使用php-sdk的话,流程是将文件上传到了服务器一次了,然后php在服务器中调用了阿里的sdk再将文件传到了OSS,小文件就无所谓了。要是上传大文件就很麻烦,服务器,php都得对大文件进行配置。过大的话还需要进行分片,可能才会有更好的上传体验。
  2. 使用后端签名,或者使用sts服务这种的,相当于是浏览器直接与阿里云进传输了,对大文件上传时比较友好的。

阿里云OSS文件上传几种方法(主要是前端)的更多相关文章

  1. 构建基于阿里云OSS文件上传服务

    转载请注明来源:http://blog.csdn.net/loongshawn/article/details/50710132 <构建基于阿里云OSS文件上传服务> <构建基于OS ...

  2. 记一次阿里云oss文件上传服务假死

    引言 记得以前刚开始学习web项目的时候,经常涉及到需要上传图片啥的,那时候都是把图片上传到当前项目文件夹下面,每次项目一重启图片就丢了.虽然可以通过修改/tomcat/conf/server.xml ...

  3. PHP实现阿里云OSS文件上传(支持批量)

    上传文件至阿里云OSS,整体逻辑是,文件先临时上传到本地,然后在上传到OSS,最后删除本地的临时文件(也可以不删,具体看自己的业务需求),具体实现流程如下:   1.下载阿里云OSS对象上传SDK(P ...

  4. SpringBoot整合阿里云OSS文件上传、下载、查看、删除

    1. 开发前准备 1.1 前置知识 java基础以及SpringBoot简单基础知识即可. 1.2 环境参数 开发工具:IDEA 基础环境:Maven+JDK8 所用技术:SpringBoot.lom ...

  5. php阿里云oss文件上传

    php的文件上传 文件上传 php的文件上传放在了$_FILES数组里,单文件和多文件上传的区别在于$_FILES['userfile']['name']是否为数组, 不熟悉的可以读一下官方文档 单文 ...

  6. 阿里云OSS文件上传封装

    1.先用composer安装阿里云OSS的PHPSDK 2.配置文件里定义阿里云OSS的秘钥 3.在index控制器里的代码封装 <?php namespace app\index\contro ...

  7. 记录-阿里云Oss文件上传

    public class OssUtil { /** * 上传图片 * @param file * @param request * @return */ public static Map<S ...

  8. Thinkphp整合阿里云OSS图片上传实例

    Thinkphp3.2整合阿里云OSS图片上传实例,图片上传至OSS可减少服务器压力,节省宽带,安全又稳定,阿里云OSS对于做负载均衡非常方便,不用传到各个服务器了 首先引入阿里云OSS类库 < ...

  9. SpringBoot完美配置阿里云的文件上传

    新建一个config类 AliyunOSS.java @Configuration @Data public class AliyunOSS { private OSSClient ossClient ...

  10. 阿里云OSS图片上传类

    1.阿里云基本函数 /** * 把本地变量的内容到文件 * 简单上传,上传指定变量的内存值作为object的内容 */ public function putObject($imgPath,$obje ...

随机推荐

  1. 记一次查询优化,mybatis查询oracle卡,直接拿sql数据库查询很快

    调整前 <select id="getList" resultMap="BaseResultMap" parameterType="java.u ...

  2. nuxt环境目录结构

  3. WPF中封装一个自己的MessageBox

    前言 在WPF应用程序开发中,我们可以借助其强大灵活的设计能力打造出绚丽而富有创意的用户界面.然而,与这种高度定制化的界面相比,标准MessageBox却显得有些原始和古老.它的外观与现代.绚丽的应用 ...

  4. Jmeter线程组-上

    线程组 线程组作为JMeter测试计划的核心组件之一,对于模拟并发用户的行为至关重要.线程组元件是整个测试计划的入口,所有的取样器和控制器必须放置在线程组下. 可以将线程组视为一个虚拟用户池,其中每个 ...

  5. 16 JavaScript逗号运算符

    16 JavaScript逗号运算符 Python 逗号运算符一般用于组合多个表达式,其返回值是最后一个表达式的值,例如: function s(){ console.log(1), console. ...

  6. #搜索#AT2368 [AGC013B] Hamiltonish Path

    题目 求一条简单路径使得路径端点不能再被延伸 分析 一开始想到可能和度数有关,其实没必要, 随便以一个点作为路径中的点深搜两次即可 代码 #include <cstdio> #includ ...

  7. #欧拉函数#洛谷 2303 [SDOI2012] Longge 的问题

    题目 求\(\sum_{i=1}^n\gcd(n,i)\) 分析 \(=\sum_{i=1}^n\sum_{d|gcd(n,i)}\varphi(d)\) \(=\sum_{d|n}\varphi(d ...

  8. 本周四晚19:00知识赋能第4期直播丨OpenHarmony智能家居项目之设备控制实现

    OpenAtom OpenHarmony(以下简称"OpenHarmony")开源开发者成长计划项目自 2021 年 10 月 24 日上线以来,在开发者中引发高度关注. 成长计划 ...

  9. Numpy数组索引和切片

    数组可以通过索引或切片的方式进行访问或修改,数组切片x[start:stop:step],与Ptyhon内置的list标准索引和切片类似,只是数组产生的是一个非副本视图,根据条件索引的值如果修改,直接 ...

  10. 开发指导—利用CSS动画实现HarmonyOS动效(二)

      注:本文内容分享转载自HarmonyOS Developer官网文档 点击查看<开发指导-利用CSS动画实现HarmonyOS动效(一)> 3. background-position ...