Dai Chong's blog

业务逻辑

原有业务

和数据直传到 OSS 相比,以上方法有三个缺点:

  • 上传慢:用户数据需先上传到应用服务器,之后再上传到OSS。网络传输时间比直传到OSS多一倍。如果用户数据不通过应用服务器中转,而是直传到OSS,速度将大大提升。而且OSS采用BGP带宽,能保证各地各运营商之间的传输速度。
  • 扩展性差:如果后续用户多了,应用服务器会成为瓶颈。
  • 费用高:需要准备多台应用服务器。由于OSS上传流量是免费的,如果数据直传到OSS,不通过应用服务器,那么将能省下几台应用服务器。
代替业务

使用Oss web直传,能有效的解决上传速度过慢的问题。

实践

php后端签名代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
/**
* 阿里Oss上传验签类
*/
class OssPolicy
{
private function gmtIso8601($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";
}

/**
* 获取签名
*/
public function policy()
{
$id = Config('aliyun.webOss.accessKeyId'); // oss accessKeyId
$key = Config('aliyun.webOss.accessKeySecret'); // oss accessKeySecret
$host = Config('aliyun.webOss.host'); // oss 上传地址
$callbackUrl = Config('aliyun.webOss.callbackUrl');// 回调地址 这个可以忽略
$dir = Config('aliyun.webOss.dir'); // 存放的文件夹

$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;
$end = $now + $expire;
$expiration = $this->gmtIso8601($end);

$condition = array(0 => 'content-length-range', 1 => 0, 2 => 1048576000);
$conditions[] = $condition;

$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;
return $response;
}
}
封装组件 ossSingleUpload.vue
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
<template>
<div>
<el-upload
list-type="picture-card"
class="avatar-uploader"
action="http://daichongwbe.oss-cn-beijing.aliyuncs.com" // 上传地址
:data="dataObj"
accept=".jpeg, .png, .jpg" // 允许上传的文件
:multiple="false" // 禁止多图上传
:before-upload="beforeUpload" // 上传前回调
:on-success="handleUploadSuccess" // 上传成功后回调
:limit="limit" // 限制上传数量
:show-file-list="false" // 禁止列表展示
ref="upload"
>
<img v-if="imageUrl" :src="imageUrl" class="avatar" />
<i v-else class="el-icon-plus avatar-uploader-icon"></i>
</el-upload>
</div>
</template>
<script>
import { getPolicy } from "@/api/oss/token"; // 后端验签接口
export default {
name: "ossSingleUpload", //组件命名
props: {
limit: Number, // 参数
imgCover: String // 参数
},
data() {
return {
dataObj: {
policy: "",
signature: "",
key: "",
ossaccessKeyId: "",
dir: "",
host: "",
callback: ""
},
imageUrl: ""
};
},
created() {
setTimeout(() => {
this.imageUrl = this.imgCover;
}, 500);
},
methods: {
emitInput(val) { // 组件交互
this.$emit("input", val);
},
beforeUpload(file) { // 上传前钩子
let _self = this;
return new Promise((resolve, reject) => {
getPolicy()
.then(response => {
_self.dataObj.policy = response.data.policy;
_self.dataObj.signature = response.data.signature;
_self.dataObj.ossaccessKeyId = response.data.accessid;
_self.dataObj.key = response.data.dir + "${filename}";
_self.dataObj.dir = response.data.dir;
_self.dataObj.host = response.data.host;
_self.dataObj.callback = response.data.callback;
resolve(true);
})
.catch(err => {
console.log(err);
reject(false);
});
});
},
handleUploadSuccess(res, file) { // 上传成功后
// 清除上次的上传记录,这里如果不清除会导致第二次上传无效。
this.$refs.upload.clearFiles();
// 组装图片路径,这里根据业务需要来。
this.imageUrl = this.dataObj.host + "/" + this.dataObj.dir + file.name;
// 组件交互,把图片路径发送给页面
this.$emit("getMessage", this.imageUrl);
}
}
};
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
.avatar-uploader .el-upload {
border: 1px dashed #d9d9d9;
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
}
.avatar-uploader .el-upload:hover {
border-color: #409eff;
}
.avatar-uploader-icon {
width: 100%;
font-size: 28px;
color: #8c939d;
text-align: center;
}
.avatar {
width: 100%;
display: block;
}
页面调用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<template>
<div class="app-container">
<oss-single-upload @getMessage="coverImg" :imgCover="imgCover" :limit="1"></oss-single-upload>
</div>
</template>

<script>
import ossSingleUpload from "@/components/Upload/ossSingleUpload"; // 引入组件
export default {
components: { ossSingleUpload },
data() {
return {
imgCover:""
};
},
methods: {
coverImg(imgUrl) {
console.log(imgUrl); //得到的上传图片路径
}
}
};
</script>

请求结果

在callbackurl为空或者callbackurl文件内容错误的情况下,这里是没有返回值的。

效果展示

github仓库

点击查看源代码


 评论