关于大文件上传,很简单,但是做起来也会碰到各种各样的问题,刚好做了一个,现在记录下过程
公司新项目要做一个大文件上传功能,将一些工程项目模型之类的东西保存在线上,动辄上G的文件肯定不能保存在自己的服务器上面,使用阿里云的oss 服务。期间经历了以下几个方法
方法一:文件从客户端分片上传到服务器。服务器再进行分片上传到oss,已实现,
缺点:
1:文件在上传过程中占用服务器带宽。
2:用户上传的大文件可能删除不及时,在服务器成为垃圾文件,占用系统磁盘。需要另外写定时任务脚本清除。
优点:敏感数据不会暴露。
方法二:web 端直接上传 ,已实现。
缺点:临时 key, secret 会暴露给客户端,有一定风险。
优点:不会占用服务器资源,直接用户端跟oss 交互。
使用webuploader插件 进行分片上传到oss
先引入插件内容
<script src="/public/others/jquery.js"></script>
<script src="/public/webuploader/webuploader.js"></script>
<link rel ="slylesheet" type="text/css" href="/public/webuploader/webuploader.css">
html代码部分
<div class="demo">
<div id="uploadfile">
<!--用来存放文件信息-->
<div id="the_2655" class="uploader-list"></div>
<div class="form-group form-inline">
<div id="pick_2655" style="float:left">选择文件</div>
<button id="Btn_2655" class="btn btn-default" style="padding: 5px 10px;border-radius: 3px;">开始上传</button>
</div>
</div>
</div>
js 部分
//上传文件函数
//ids唯一ID
//folder文件保存目录
function uploadfiles(ids,folder) {
$(function(){
var $list = $("#the_"+ids);
$btn = $("#Btn_"+ids);
var uploader = WebUploader.create({
resize: false, // 不压缩image
swf: '__PUBLIC__/ueditor/third-party/webuploader/uploader.swf', // swf文件路径
server: '{:url("admin/upload/uploadFile")}', // 文件接收服务端。
pick: "#pick_"+ids, // 选择文件的按钮。可选
chunked: true, //是否要分片处理大文件上传
chunkSize:5*1024*1024, //分片上传,每片2M,默认是5M
//fileSizeLimit: 6*1024* 1024 * 1024, // 所有文件总大小限制 6G
fileSingleSizeLimit: 10*1024* 1024 * 1024, // 单个文件大小限制 5 G
formData: {
folder:folder //自定义参数
}
//auto: false //选择文件后是否自动上传
// chunkRetry : 2, //如果某个分片由于网络问题出错,允许自动重传次数
//runtimeOrder: 'html5,flash',
// accept: {
// title: 'Images',
// extensions: 'gif,jpg,jpeg,bmp,png',
// mimeTypes: 'image/*'
// }
});
// 当有文件被添加进队列的时候
uploader.on( 'fileQueued', function( file ) {
$list.append( '+ file.id + '" class="item">' +
''
+ file.name + '' +
'等待上传...
' +
'' );
});
// 文件上传过程中创建进度条实时显示。
uploader.on( 'uploadProgress', function( file, percentage ) {
var $li = $( '#'+file.id ),
$percent = $li.find('.progress .progress-bar');
// 避免重复创建
if ( !$percent.length ) {
$percent = $('' +
' '+
'').appendTo( $li ).find('.progress-bar');
}
$li.find('p.state').text('上传中');
$percent.css( 'width', percentage * 100 + '%' );
});
// 文件上传成功
uploader.on( 'uploadSuccess', function( file,response) {
$( '#'+file.id ).find('p.state').text('已上传');
$list.append('+ids+'" value="'+response.filePath+'" />');
//alert(response.filePath);
});
// 文件上传失败,显示上传出错
uploader.on( 'uploadError', function( file ) {
$( '#'+file.id ).find('p.state').text('上传出错');
});
// 完成上传完
uploader.on( 'uploadComplete', function( file ) {
$( '#'+file.id ).find('.progress').fadeOut();
});
$btn.on('click', function () {
if ($(this).hasClass('disabled')) {
return false;
}
uploader.upload();
// if (state === 'ready') {
// uploader.upload();
// } else if (state === 'paused') {
// uploader.upload();
// } else if (state === 'uploading') {
// uploader.stop();
// }
});
});
}
注意demo 里边分片的每片文件大小是5M,php 的ini 文件里边最大限制的参数要大于5M,才能上传成功,具体怎么修改的话去百度。
php 部分
/**
* 上传文件函数,如过上传不成功打印$_FILES数组,查看error报错信息
* 值:0; 没有错误发生,文件上传成功。
* 值:1; 上传的文件超过了 php.ini 中 upload_max_filesize 选项限制的值。
* 值:2; 上传文件的大小超过了 HTML 表单中 MAX_FILE_SIZE 选项指定的值。
* 值:3; 文件只有部分被上传。
* 值:4; 没有文件被上传。
* date:2018.4.18 from:zhix.net
*/
public function uploadFile(){
header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
header("Content-type: text/html; charset=gbk32");
header("Cache-Control: no-store, no-cache, must-revalidate");
header("Cache-Control: post-check=0, pre-check=0", false);
header("Pragma: no-cache");
$folder = input('folder');
if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') {
exit; // finish preflight CORS requests here
}
if ( !empty($_REQUEST[ 'debug' ]) ) {
$random = rand(0, intval($_REQUEST[ 'debug' ]) );
if ( $random === 0 ) {
header("HTTP/1.0 500 Internal Server Error");
exit;
}
}
// header("HTTP/1.0 500 Internal Server Error");
// exit;
// 5 minutes execution time
set_time_limit(5 * 60);
// Uncomment this one to fake upload time
usleep(5000);
// Settings
$targetDir = Env::get('root_path').'/upload/test/file_material_tmp'; //存放分片临时目录
if($folder){
$uploadDir =Env::get('root_path').'/upload/test/file_material/';
}else{
$uploadDir = Env::get('root_path').'/upload/test/file_material/'; //分片合并存放目录
}
$cleanupTargetDir = true; // Remove old files
$maxFileAge = 5 * 3600; // Temp file age in seconds
// Create target dir
if (!file_exists($targetDir)) {
mkdir($targetDir,0777,true);
}
// Create target dir
if (!file_exists($uploadDir)) {
mkdir($uploadDir,0777,true);
}
// Get a file name
if (isset($_REQUEST["name"])) {
$fileName = $_REQUEST["name"];
} elseif (!empty($_FILES)) {
$fileName = $_FILES["file"]["name"];
} else {
$fileName = uniqid("file_");
}
$oldName = $fileName;
$fileName = iconv('UTF-8','gb2312',$fileName);
$filePath = $targetDir . DIRECTORY_SEPARATOR . $fileName;
// $uploadPath = $uploadDir . DIRECTORY_SEPARATOR . $fileName;
// Chunking might be enabled
$chunk = isset($_REQUEST["chunk"]) ? intval($_REQUEST["chunk"]) : 0;
$chunks = isset($_REQUEST["chunks"]) ? intval($_REQUEST["chunks"]) : 1;
// Remove old temp files
if ($cleanupTargetDir) {
if (!is_dir($targetDir) || !$dir = opendir($targetDir)) {
die('{"jsonrpc" : "2.0", "error" : {"code": 100, "message": "Failed to open temp directory111."}, "id" : "id"}');
}
while (($file = readdir($dir)) !== false) {
$tmpfilePath = $targetDir . DIRECTORY_SEPARATOR . $file;
// If temp file is current file proceed to the next
if ($tmpfilePath == "{$filePath}_{$chunk}.part" || $tmpfilePath == "{$filePath}_{$chunk}.parttmp") {
continue;
}
// Remove temp file if it is older than the max age and is not the current file
if (preg_match('/\.(part|parttmp)$/', $file) && (filemtime($tmpfilePath) < time() - $maxFileAge)) {
unlink($tmpfilePath);
}
}
closedir($dir);
}
// Open temp file
if (!$out = fopen("{$filePath}_{$chunk}.parttmp", "wb")) {
die('{"jsonrpc" : "2.0", "error" : {"code": 102, "message": "Failed to open output stream222."}, "id" : "id"}');
}
if (!empty($_FILES)) {
if ($_FILES["file"]["error"] || !is_uploaded_file($_FILES["file"]["tmp_name"])) {
die('{"jsonrpc" : "2.0", "error" : {"code": 103, "message": "Failed to move uploaded file333."}, "id" : "id"}');
}
// Read binary input stream and append it to temp file
if (!$in = fopen($_FILES["file"]["tmp_name"], "rb")) {
die('{"jsonrpc" : "2.0", "error" : {"code": 101, "message": "Failed to open input stream444."}, "id" : "id"}');
}
} else {
if (!$in = fopen("php://input", "rb")) {
die('{"jsonrpc" : "2.0", "error" : {"code": 101, "message": "Failed to open input stream555."}, "id" : "id"}');
}
}
while ($buff = fread($in, 4096)) {
fwrite($out, $buff);
}
fclose($out);
fclose($in);
rename("{$filePath}_{$chunk}.parttmp", "{$filePath}_{$chunk}.part");
$index = 0;
$done = true;
for( $index = 0; $index < $chunks; $index++ ) {
if ( !file_exists("{$filePath}_{$index}.part") ) {
$done = false;
break;
}
}
if ($done) {
$pathInfo = pathinfo($fileName);
$hashStr = substr(md5($pathInfo['basename']),8,16);
$hashName = time() . $hashStr . '.' .$pathInfo['extension'];
$uploadPath = $uploadDir . DIRECTORY_SEPARATOR .$hashName;
if (!$out = fopen($uploadPath, "wb")) {
die('{"jsonrpc" : "2.0", "error" : {"code": 102, "message": "Failed to open output stream666."}, "id" : "id"}');
}
//flock($hander,LOCK_EX)文件锁
if ( flock($out, LOCK_EX) ) {
for( $index = 0; $index < $chunks; $index++ ) {
if (!$in = fopen("{$filePath}_{$index}.part", "rb")) {
break;
}
while ($buff = fread($in, 4096)) {
fwrite($out, $buff);
}
fclose($in);
unlink("{$filePath}_{$index}.part");
}
flock($out, LOCK_UN);
}
fclose($out);
$response = [
'success'=>true,
'oldName'=>$oldName,
'filePath'=>$uploadPath,
// 'fileSize'=>$data['size'],
'fileSuffixes'=>$pathInfo['extension'], //文件后缀名
// 'file_id'=>$data['id'],
];
return json($response);
}
// Return Success JSON-RPC response
die('{"jsonrpc" : "2.0", "result" : null, "id" : "id"}');
}
后端接收分片,并且组合文件的方法。注意几点
1:方法的header 头里边要加上 utf8的限制,如下,不然文件名称为中文会乱码
2:返回的路径是绝对路径,自己根据项目需求自己截取。
header("Content-type: text/html; charset=utf-8");
文件上传部分:
if (is_file(__DIR__ . '/../autoload.php')) {
require_once __DIR__ . '/../autoload.php';
}
if (is_file(__DIR__ . '/../vendor/autoload.php')) {
require_once __DIR__ . '/../vendor/autoload.php';
}
use OSS\OssClient;
use OSS\Core\OssException;
// 阿里云主账号AccessKey拥有所有API的访问权限,风险很高。强烈建议您创建并使用RAM账号进行API访问或日常运维,请登录 https://ram.console.aliyun.com 创建RAM账号。
$accessKeyId = "" ;
$accessKeySecret = "" ;
// Endpoint以杭州为例,其它Region请按实际情况填写。
$endpoint = "http://oss-cn-hangzhou.aliyuncs.com";
$bucket= "" ;
$object = "" ;
$file = "" ;
$options = array(
OssClient::OSS_CHECK_MD5 => true,
OssClient::OSS_PART_SIZE => 1,
);
try{
$ossClient = new OssClient($accessKeyId, $accessKeySecret, $endpoint);
$ossClient->multiuploadFile($bucket, $object, $file, $options);
} catch(OssException $e) {
printf(__FUNCTION__ . ": FAILED\n");
printf($e->getMessage() . "\n");
return;
}
print(__FUNCTION__ . ": OK" . "\n");
注意 multiuploadFile 方法,该方法就是分片上传的具体封装的方法,阿里云分片上传是上传一个具体的文件,你自己的每一个分片阿里云是不承认的,所以不要自己尝试上传分片文件。
具体使用方法参考链接:
阿里云php oss分片上传描述文档
上传结果参考文档。
web 直传有个问题就是需要 STS临时授权访问
你需要了解这个玩意
具体就是阿里云总帐号创建一个子账号,子账号被赋予了oss 的权限,然后拥有oss 权限的子账号相当于一个新的RAM角色,通过这个子账号跟角色创建时候生成的 id和 secret 可以获取一个临时token,通过这个token,前端在不获取总账号权限的情况下可以进行一些操作。具体创建子账号的流程可以看上边链接,赋予的权限选择前端要使用的,根据自己实际情况。
阿里云有给到一个获取STS临时授权访问 的接口文档,
阿里云获取sts
注意这里的version 2015-04-01, 各种尝试不通,报错如下:
sts.aliyuncs.comInvalidVersionSpecified parameter Version is not valid.
提工单,回复如下:
我dnmd,不能用你挂这里干嘛?浪费谁的时间?
附:请求接口的方法:
class STS
{
protected $url = 'https://sts.aliyuncs.com';
protected $accessKeySecret = '1234567890qwertyuioasdfghj';
protected $accessKeyId = 'LT11234567898';
protected $roleArn = 'acs:ram::$accountID:role/$roleName';//指定角色的 ARN ,角色策略权限
protected $roleSessionName = 'client1';//用户自定义参数。此参数用来区分不同的 token,可用于用户级别的访问审计。格式:^[a-zA-Z0-9\.@\-_]+$
protected $durationSeconds = '1800';//指定的过期时间
protected $type = 'xxx';//方便调用时获取不同的权限
public function __construct($type)
{
$this->type = $type;
$this->setRoleArn();
}
public function sts()
{
$action = 'AssumeRole';//通过扮演角色接口获取令牌
date_default_timezone_set('UTC');
$param = array(
'Format' => 'JSON',
'Version' => '2015-04-01',
'AccessKeyId' => $this->accessKeyId,
'SignatureMethod' => 'HMAC-SHA1',
'SignatureVersion' => '1.0',
'SignatureNonce' => $this->getRandChar(8),
'Action' => $action,
'RoleArn' => $this->roleArn,
'RoleSessionName' => $this->roleSessionName,
'DurationSeconds' => $this->durationSeconds,
'Timestamp' => date('Y-m-d') . 'T' . date('H:i:s') . 'Z'
//'Policy'=>'' //此参数可以限制生成的 STS token 的权限,若不指定则返回的 token 拥有指定角色的所有权限。
);
$param['Signature'] = $this->computeSignature($param, 'POST');
$res = CurlHandle::httpPost($this->url, $param);//curl post请求
if ($res) {
return self::_render($res);
} else {
return [];
}
}
private static function _render($res)
{
$res = json_decode($res, true);
if (empty($res['Credentials'])) {
return [];
} else {
return [
'accessKeySecret' => $res['Credentials']['AccessKeySecret'] ?? '',
'accessKeyId' => $res['Credentials']['AccessKeyId'] ?? '',
'expiration' => $res['Credentials']['Expiration'] ?? '',
'securityToken' => $res['Credentials']['SecurityToken'] ?? '',
];
}
}
protected function computeSignature($parameters, $setMethod)
{
ksort($parameters);
$canonicalizedQueryString = '';
foreach ($parameters as $key => $value) {
$canonicalizedQueryString .= '&' . $this->percentEncode($key) . '=' . $this->percentEncode($value);
}
$stringToSign = $setMethod . '&%2F&' . $this->percentencode(substr($canonicalizedQueryString, 1));
$signature = $this->getSignature($stringToSign, $this->accessKeySecret . '&');
return $signature;
}
public function getSignature($source, $accessSecret)
{
return base64_encode(hash_hmac('sha1', $source, $accessSecret, true));
}
protected function percentEncode($str)
{
$res = urlencode($str);
$res = preg_replace('/\+/', '%20', $res);
$res = preg_replace('/\*/', '%2A', $res);
$res = preg_replace('/%7E/', '~', $res);
return $res;
}
public function getRandChar($length)
{
$str = null;
$strPol = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz";
$max = strlen($strPol) - 1;
for ($i = 0; $i < $length; $i++) {
$str .= $strPol[rand(0, $max)];//rand($min,$max)生成介于min和max两个数之间的一个随机整数
}
return $str;
}
protected function setRoleArn()
{
if ($this->type == '123') {//根据入参使用不同的策略,当然这里还可以有其他写法兼容更多的策略的情况
$this->roleArn = 'acs:ram::123456789098:role/=$roleName';
}
}
}
还是要使用sdk ,不过这个sdk 是阿里云已经给封装好的该模块的sdk ,比自己从github里边下载的好使一万倍。tp 的话就放到vendor 下边就行,laravel 的话自己看,能正常引入就行。
STS的SDK下载链接
下载完成之后引入项目
简单的请求token 的方法,
1:注意过期时间,默认是3600s,
2:accessKeySecret,accessKeyId,这两个参数是你创建的子账号的参数,不是总账号的。
namespace app\services;
class StsService
{
protected $url = 'https://sts.aliyuncs.com';
protected $accessKeySecret;
protected $accessKeyId;
protected $roleArn;//指定角色的 ARN ,角色策略权限
protected $roleSessionName = 'client';//用户自定义参数。此参数用来区分不同的 token,可用于用户级别的访问审计。格式:^[a-zA-Z0-9\.@\-_]+$
protected $durationSeconds = '3600';//指定的过期时间
public function __construct()
{
$this->accessKeySecret = config('oss_sts_accessKeySecret');
$this->accessKeyId = config('oss_sts_accessKeyId');
$this->roleArn = config('oss_sts_roleArn');
}
public function getStsOuah()
{
require_once VENDOR_PATH.'aliyuncs/sts-server/aliyun-php-sdk-core/Config.php';
$iClientProfile = \DefaultProfile::getProfile("cn-hangzhou", $this->accessKeyId, $this->accessKeySecret);
$client = new \DefaultAcsClient($iClientProfile);
$request = new \Sts\Request\V20150401\AssumeRoleRequest();
$request->setRoleSessionName("client_name");
$request->setRoleArn($this->roleArn);
// $request->setPolicy(VENDOR_PATH.'aliyuncs/sts-server/policy/bucket_write_policy.txt');
$request->setDurationSeconds($this->durationSeconds);
$response = $client->doAction($request);
$rows = array();
$body = $response->getBody();
$content = json_decode($body);
if ($response->getStatus() == 200){
$rows['statusCode'] = 200;
$rows['accessKeyId'] = $content->Credentials->AccessKeyId;
$rows['accessKeySecret'] = $content->Credentials->AccessKeySecret;
$rows['expiration'] = $content->Credentials->Expiration;
$rows['securityToken'] = $content->Credentials->SecurityToken;
}else{
$rows['statusCode'] = 500;
$rows['errorCode'] = $content->Code;
$rows['errorMessage'] = $content->Message;
}
return $rows;
}
}
返回的数据,需要把这些数据传给前端
Array
(
[statusCode] => 200
[accessKeyId] => STS.NT3aC8UXWJydcuuoyreGfvWxJ
[accessKeySecret] => 59vT1iGHaJtEiyDwpEN9125ZgBJ4eShfUbtnXiGvQirb
[expiration] => 2020-06-06T06:11:20Z
[securityToken] => CAIS9QF1q6Ft5B2yfSjIr5eGKvmMuId2/buzYVPEi3k2achKmZLTqDz2IHlPdHlrBOwesv8/lGFY7fwblqJ4T55IQ1Dza8J148z5Be4Lo8yT1fau5Jko1beHewHleTOZsebWZ+LmNqC/Ht6md1HDkAJq3LL+bk/Mdle5MJqP+/UFB5ZtKWveVzddA8pMLQZPsdITMWCrVcygKRn3mGHdfiEK00he8TonsPXhn5fEskSF3QOhlbAvyt6vcsT+Xa5FJ4xiVtq55utye5fa3TRYgxowr/4t3PwfpGuf4orNUwgBuE/fKYnd/thuKh5kYLM6Gr71cwRkHn730xqAARfcIG9I3u0E7TJzfmTvcIIIpogdqzjed27c1JvZAoAxHu+6XgIBgrbcLds0q2InwBPKWF4S0nfKnI9kXPn1CXtKdjQmgLW0cRz0w9/OfbxTIX9lb2HmtWABxvLVAgQtHJRb4o6xxgkQy5pBs3BeHYJ1yY9NZCDu5zW2OGC03MqZ
)
前端代码:
本人 不是专业前端,样式凑合着看吧。
html
<div id="up_wrap"></div>
<div class="form-group">
<input type="file" id="file" multiple="multiple" />
</div>
<div class="form-group">
<input type="button" class="btn btn-primary" id="file-button" value="Upload" />
<input type="button" class="btn btn-primary" id="Continue-button" value="Continue" />
</div>
js
<script src="http://gosspublic.alicdn.com/aliyun-oss-sdk-6.0.2.min.js"></script>
<script type="text/javascript">
var appServer = 'http://www.tele.bh/med/home/sts';//获取ststoken的接口,这边这个地址是我本地的。你们的接口地址自己应该清楚
var bucket = '你新建的bucket名字';
var region = 'oss-cn-hangzhou';//前面新建bucket时选择过的。
var uid = 'x';//用户标识。这个根据自己情况自己定
var Buffer = OSS.Buffer;
console.log(OSS);
//获取授权STSToken,并初始化client
var applyTokenDo = function (func) {
var url = appServer;
return $.get(url).then(function (result) {
var res = JSON.parse(result);
var creds = res.data.Credentials;
console.log(creds);
var client = new OSS({
region: region,
accessKeyId: creds.AccessKeyId,
accessKeySecret: creds.AccessKeySecret,
stsToken: creds.SecurityToken,
bucket: bucket
});
return func(client);
});
};
//上传文件
var uploadFile = function (client) {
if (upfiles.length < 1)
return;
upfile = upfiles[0];
var file = upfile.file;
//key可以自定义为文件名(例如file.txt)或目录(例如abc/test/file.txt)的形式,实现将文件上传至当前Bucket或Bucket下的指定目录。
var key = upfile.name;
var objkey = key + "_" + uid + ".json";
return client.multipartUpload(key, file, {
progress: function (p, cpt, res) {
console.log("p:", p);
console.log("cpt:", cpt);
if (cpt != undefined) {
var content = JSON.stringify(cpt);
client.put(objkey, new Buffer(content));
}
console.log(Math.floor(p * 100) + '%');
var bar = document.getElementById('progress-bar_' + upfile.num);
bar.style.width = Math.floor(p * 100) + '%';
bar.innerHTML = Math.floor(p * 100) + '%';
}
}).then(function (res) {
console.log('upload success: ', res);
upfiles.shift();
client.delete(objkey);
applyTokenDo(uploadFile);
}).catch(function(err) {
console.log(err);
error(err);
});
};
//断点续传文件
var reUploadFile = function (client) {
if (upfiles.length < 1)
return;
upfile = upfiles[0];
var file = upfile.file;
//key可以自定义为文件名(例如file.txt)或目录(例如abc/test/file.txt)的形式,实现将文件上传至当前Bucket或Bucket下的指定目录。
var key = upfile.name;
var objkey = key + "_" + uid + ".json";
return client.get(objkey).then(function (res) {
var data = JSON.parse(res.content);
data.file = file;
return client.multipartUpload(key, file, {
checkpoint: data,
progress: function (p, cpt, res) {
console.log("p:", p);
console.log("cpt:", cpt);
if (cpt != undefined) {
var content = JSON.stringify(cpt);
client.put(objkey, new Buffer(content));
}
var bar = document.getElementById('progress-bar_' + upfile.num);
bar.style.width = Math.floor(p * 100) + '%';
bar.innerHTML = Math.floor(p * 100) + '%';
}
}).then(function (ret) {
console.log('upload success:', ret);
upfiles.shift();
client.delete(objkey);
applyTokenDo(uploadFile);
}).catch(function(err) {
console.log(err);
error(err);
});
});
};
function error(err){
switch (err.status) {
case 0:
if (err.name == "cancel") { //手动点击暂停上传
return;
}
break;
case -1: //请求错误,自动重新上传
// 重新上传;
return;
case 203: //回调失败
// 前端自己给后台回调;
return;
case 400:
switch (err.code) {
case 'FilePartInterity': //文件Part已改变
case 'FilePartNotExist': //文件Part不存在
case 'FilePartState': //文件Part过时
case 'InvalidPart': //无效的Part
case 'InvalidPartOrder': //无效的part顺序
case 'InvalidArgument': //参数格式错误
// 清空断点;
// 重新上传;
return;
case 'InvalidBucketName': //无效的Bucket名字
case 'InvalidDigest': //无效的摘要
case 'InvalidEncryptionAlgorithmError': //指定的熵编码加密算法错误
case 'InvalidObjectName': //无效的Object名字
case 'InvalidPolicyDocument': //无效的Policy文档
case 'InvalidTargetBucketForLogging': //Logging操作中有无效的目标bucket
case 'MalformedXML': //XML格式非法
case 'RequestIsNotMultiPartContent': //Post请求content-type非法
// 重新授权;
// 继续上传;
return;
case 'RequestTimeout'://请求超时
// 重新上传;
return;
}
break;
case 403: //授权无效,重新授权
case 411: //缺少参数
case 404: //Bucket/Object/Multipart Upload ID 不存在
// 重新授权;
// 继续上传;
return;
case 500: //OSS内部发生错误
// 重新上传;
return;
default:
break;
}
}
//文件上传队列
var upfiles = [];
$(function () {
//初始化文件上传队列
$("#file").change(function (e) {
var ufiles = $(this).prop('files');
var htm = "";
for (var i = 0; i < ufiles.length; i++) {
htm += "- "
+ ufiles[i].name + " + i + "\" class=\"progress-bar\" role=\"progressbar\" aria-valuenow=\"0\" aria-valuemin=\"0\" aria-valuemax=\"100\" style=\"min-width: 2em;\">0% ";
upfiles.push({
num: i,
name: ufiles[i].name,
file: ufiles[i]
})
}
console.log('upfiles:', upfiles);
$("#up_wrap").html(htm);
});
//上传
$("#file-button").click(function () {
applyTokenDo(uploadFile);
});
//续传
$("#Continue-button").click(function () {
applyTokenDo(reUploadFile);
})
})
</script>
分片上传完成之后会返回带uploadId的文件夹链接,上传给后端就行了。
注意几点,
1:角色的权限,RAM 设置的时候要给put, delete 请求权限,不然js会报错。
2:跨域访问:角色设置的时候要有 Access-Control-Allow-Origin header头,允许跨域访问。
功能没那么难,问题出在要么阿里云给的给的文档不好使,要么sdk 难用。本人也是综合了好几个百度出来的文档才搞出来的,分享出来希望以后大家少走弯路吧。