导火索

页面上传至七牛云最常采用的方式是“页面 -> 服务器 -> 七牛云”,也就是前端先上传至服务器,然后后端进行上传至七牛云,这样做的好处时,中断率出错率会相对比较低,但如果时比较大的文件,那简直就浪费时间,整个过程会非常慢,在此同时会消耗服务器资源,带宽占用、负载变大等,这样非常不友好;
然而我们可以直接采用页面直传至七牛云,即“页面 -> 七牛云”,这样对应上传大文件来说,只要客户端网络好,上传就很快,但这时消耗的客户端资源,也就说若客户端配置较差有可能会导致卡顿等;

一、“页面 -> 七牛云”上传

1、 后端需提供七牛云上传token,注意每次上传token都不一样,所以每次上传需更新token;

以PHP为例,以下是七牛云 PHP SKD 官方文档 ,其他语言对应的文档可打开后在左边菜单栏选择

https://developer.qiniu.com/kodo/1241/php

关键步骤为

use \Qiniu\Auth;
$accessKey = "QINIU_ACCESS_KEY"
$secretKey = "QINIU_SECRET_KEY"
$bucket = "your bucket name"
// 初始化Auth状态
$auth = new Auth($accessKey, $secretKey);

$expires = 3600;
$filename = 'mp4/qiniu.mp4';//上传至七牛云空间的路径与名称
$policy = null;//自定义需返回的参数
$upToken = $auth->uploadToken($bucket, $filename, $expires, $policy, true);

需特别注意上面的 filename 这个名字必须与前端使用七牛云JSSDK上传传入的名字一致

2、 前端将文件直接使用七牛云JSSDK 上传至七牛云

JavaScript SDK 官方文档
https://developer.qiniu.com/kodo/1283/javascript

关键步骤
  • 引入js 文件

支持以下几种安装方式

  • 直接使用静态文件地址

https://cdnjs.cloudflare.com/ajax/libs/qiniu-js/<version>/qiniu.min.js
// 当上方资源链接访问不稳定时,可选使用下方资源链接
https://cdn.staticfile.org/qiniu-js/<version>/qiniu.min.js
通过 script 标签引入该文件,会在全局生成名为 qiniu 的对象
<version>为版本号,具体可以查看版本信息 https://github.com/qiniu/js-sdk/releases
eg:https://cdn.staticfile.org/qiniu-js/3.3.3/qiniu.min.js
另外此处有坑,注意需引入JQ,且对JQ版本有要求,需jquery-3.3.1.min.js或以上版本,不然可能报错“require.js:8 Uncaught Error: Mismatched anonymous define() module: function(){return function(t){var e={};function n®”

  • 使用 NPM 安装

NPM 的全称是 Node Package Manager,是一个 NodeJS 包管理和分发工具,已经成为了非官方的发布 Node 模块(包)的标准。如果需要更详细的关于 NPM 的使用说明,您可以访问 NPM 官方网站,或对应的 中文网站
npm install qiniu-js
const qiniu = require(‘qiniu-js’)
// or
import * as qiniu from ‘qiniu-js’

  • 通过源码编译

git clone git@github.com:qiniu/js-sdk.git,进入项目根目录执行 npm install ,执行 npm run build,即可在dist 目录生成 qiniu.min.js。

  • 引入 js 后会生成全局 qiniu 对象,接下来就是使用这个对象进行上传

最关键的代码

//file上传文件
//filename 上传至七牛云的文件名称要与上面提到的生成token的文件名称一致
//token 上传token 一般向后端获取
//putExtra 额外的参数,详情请参考文档
//config 配置信息 ,详情请参考文档
qiniu.upload(file, filename, token, putExtra, config);

3、实战(使用 layui upload组件)

  • 上传html (省略不必要的 html 代码)(以下代码仅供参考)
    <button type="button" class="layui-btn" id="uploader_button">
  		<i class="layui-icon">&#xe67c;</i>上传图片
	</button>
    <script>
       layui.config({
            base: ''
        }).extend({
            qiniuyun: "../qiniuyun/index",//引入自己写的七牛云上传组件
        }).use(['qiniuyun', 'layer', 'upload', 'form', 'element'], function(){
            var upload = layui.upload
                , form = layui.form
                , element = layui.element
                ,layer = layui.layer
                ,qiniuyun = layui.qiniuyun
            ;
            //执行upload组件实例
            uploadInst = upload.render({
                elem: '#uploader_button' //绑定元素
                ,accept: "*/*" //允许上传的文件类型
                ,field: "file" //设定文件域的字段名
                ,acceptMime: "*/*"
                ,auto: false//选择文件后不自动上传
                ,progress: function(n, elem, res, index){
                  if (n >= 100) {
                           thisPercentUpdate(100);
                           thisPercent = 50;
                       }
                       var percent = (Number(n) / 2) + '%' //获取进度百分比
                       element.progress('file_uploader_progress', percent); //可配合 layui 进度条元素使用
                },
                choose: function (obj) {
                    thisPercent = 0;
                    updateThisPercent = 0;
                    $("#file_uploader_progress_div").show();
                    element.progress('file_uploader_progress', "0%");
                    layer.load(); //上传loading
                    $("#content").val("");
                    $("#file_original_name").val("");
                    $("#upload_success_file_div").hide();
                    $("#upload_success_file_name").html("未上传或上传失败 ~ ");
                    $("#file_uploader_button").removeClass('layui-btn-fz');
                    $("#file_uploader_button").css("background-color", "#009688");
                    $("#upload_success_file_name").css("color", "#000000");
                    if (selectType === 1 || 1) {
                        //start
                        var files = obj.pushFile(), thisStart = 0, keysArray = Object.keys(files);
                        if (files && keysArray.length > 0) {
                            keysArray.forEach(function(key) {
                                if (thisStart === keysArray.length - 1) {
                                    let index = key
                                        , file = files[key]
                                        , resultObj = files[key]
                                    ;
                                    let requestData = {}, thisFilename;
                                    if (file && file.name) {
                                        thisFilename = file.name;
                                        requestData = {filename: thisFilename};
                                    }
                                    $.ajax({
                                        url: "your get qiniuyun token api url",
                                        type: "POST",
                                        data: requestData,
                                        dataType: "json",
                                        success: function (result) {
                                            let thisData = result.data;
                                            if (result.code === 1 && thisData && thisData.token) {
                                                qiniuyun.loader({
                                                    domain: thisData.domain              // 后台设置的域名项
                                                    , elem: "#file_uploader_qi_niu"          // 绑定的element
                                                    , token: thisData.token              // 授权token
                                                    , retryCount: 6                  // 重连次数,默认6(可选)
                                                    , fileNamePath: thisData.path 
                                                    , fileNameUniQid: thisData.uniqid
                                                    , fileName: thisData.fileName
                                                    , originalName: thisData.originalName
                                                    , fileExt: thisData.fileExt
                                                    , validExt: thisData.ext
                                                    , validMaxSzie: thisData.maxSize
                                                    , thisFile: file
                                                    , thisObj: file
                                                    // , region: qiniu.region.z2        // 选择上传域名区域,默认自动分析(可选)
                                                    , next: function(response){
                                                        element.progress('file_uploader_progress', response.total.percent + '%');       // 进度条
                                                    }, error(err){
                                                        thisPercent = 0;
                                                        updateThisPercent = 100000;
                                                        $("#file_uploader_progress_div").hide();
                                                        element.progress('file_uploader_progress', "0%");
                                                        $("#upload_success_file_div").show();
                                                        $("#upload_success_file_name").html("上传失败了,请重新上传 ~ ");
                                                        $("#uploader_span").html("重新上传");
                                                        $("#file_uploader_button").addClass('layui-btn-fz');
                                                        $("#file_uploader_button").css("background-color", "#ba1535");
                                                        $("#upload_success_file_name").css("color", "#ba1535");
                                                        layer.alert(err, {title: "上传失败提示", icon: 2,})
                                                        layer.closeAll('loading'); //关闭loading
                                                    },complete: function(res){//上传成功
                                                        thisPercent = 100;
                                                        updateThisPercent = 100000;
                                                        element.progress('file_uploader_progress', "100%");
                                                        $("#content").val(thisData.domain+"/"+res.key);
                                                        $("#upload_success_file_div").show();
                                                        $("#upload_success_file_name").html(thisFilename);
                                                        $("#file_original_name").val(thisFilename);
                                                        $("#uploader_span").html("重新上传");
                                                        $("#file_uploader_button").addClass('layui-btn-fz');
                                                        $("#upload_success_file_name").css("color", "rgba(26,112,215,0.6)");
                                                        isUploader = 1;
                                                        layer.msg("上传成功 ~",{icon: 1, time: 1500}, function () {
                                                            $("#file_uploader_progress_div").hide();
                                                        })
                                                        layer.closeAll('loading'); //关闭loading
                                                    }
                                                });
                                            } else {
                                                thisPercent = 0;
                                                updateThisPercent = 100000;
                                                $("#file_uploader_progress_div").hide();
                                                element.progress('file_uploader_progress', "0%");
                                                $("#upload_success_file_div").show();
                                                $("#upload_success_file_name").html("上传失败了,请重新上传 ~ ");
                                                $("#uploader_span").html("重新上传");
                                                $("#file_uploader_button").addClass('layui-btn-fz');
                                                $("#file_uploader_button").css("background-color", "#ba1535");
                                                $("#upload_success_file_name").css("color", "#ba1535");
                                                layer.alert("【tokenERROR2】网络异常,请重试 ~ ", {title: "上传失败提示", icon: 2,})
                                                layer.closeAll('loading'); //关闭loading
                                            }
                                        },
                                        error: function () {
                                            thisPercent = 0;
                                            updateThisPercent = 100000;
                                            $("#file_uploader_progress_div").hide();
                                            element.progress('file_uploader_progress', "0%");
                                            $("#upload_success_file_div").show();
                                            $("#upload_success_file_name").html("上传失败了,请重新上传 ~ ");
                                            $("#uploader_span").html("重新上传");
                                            $("#file_uploader_button").addClass('layui-btn-fz');
                                            $("#file_uploader_button").css("background-color", "#ba1535");
                                            $("#upload_success_file_name").css("color", "#ba1535");
                                            layer.alert("【tokenERROR1】网络异常,请重试 ~ ", {title: "上传失败提示", icon: 2,})
                                            layer.closeAll('loading'); //关闭loading
                                        }

                                    });
                                } else {
                                    layer.msg("上传时发生异常,请重试 ~ ", {icon: 2, time: 1500});
                                }
                                thisStart++;
                            });
                        }
                    }
                }
                ,error: function(){
                  	   thisPercent = 0;
                       updateThisPercent = 100000;
                       $("#file_uploader_progress_div").hide();
                       element.progress('file_uploader_progress', "0%");
                       $("#upload_success_file_div").show();
                       $("#upload_success_file_name").html("上传失败了,请重新上传 ~ ");
                       $("#uploader_span").html("重新上传");
                       $("#file_uploader_button").addClass('layui-btn-fz');
                       $("#file_uploader_button").css("background-color", "#ba1535");
                       $("#upload_success_file_name").css("color", "#ba1535");
                       layer.alert("网络异常,请重试 ~ ", {title: "上传失败提示", icon: 2,})
                       layer.closeAll('loading'); //关闭loading
                }
            });

            
        });
    </script>    
  • 自己的七牛云上传组件(以下代码仅供参考)
var checkDomain = 0;//是否校验七牛空间(bucket)对应的域名
layui.define('layer', function(exports){
	var $ = layui.$,layer = layui.layer, thisFileNamePath = "", thisFileNameUniQid = "";
	exports('qiniuyun', {
        loader:function (options,callback) {
			let thisBase = "https://cdn.staticfile.org/qiniu-js/3.3.3/qiniu.min.js" //七牛云js
				,thisJqUrl = "https://ajax.aspnetcdn.com/ajax/jQuery/jquery-3.3.1.min.js" //JQ
			;
			$.getScript(thisJqUrl,function () {//动态加载JQ 务必先加载JQ
				console.log(thisJqUrl)
				$.getScript(thisBase,function () { //动态加载七牛云JS
					//console.log(qiniu);// 可先尝试是否获取到 qiniu 对象
					if(!options.token){
						layer.msg('初始化参数:token不能为空.', {icon: 2});
						return;
					}
					if(checkDomain  && !options.domain){
						layer.msg('初始化参数:域名不能为空.', {icon: 2});
						return;
					}
					if (options.fileNamePath) {
						thisFileNamePath = options.fileNamePath;
						console.log(thisFileNamePath)
					}
					if (options.fileNameUniQid) {
						thisFileNameUniQid = options.fileNameUniQid;
						console.log(thisFileNameUniQid)
					}
					var token = options.token
						, domain = options.domain
						, config = {
							useCdnDomain: true, //是否使用加速域名
							disableStatisticsReport: false, //是否禁用日志报告
							retryCount: options.retryCount || 6,
							debugLogLevel: "INFO",
							region: options.region || qiniu.region.z2
						}
						, putExtra = {
							fname: "",
							params: {},
							mimeType: null
						}
						, thisFile
						, thisObj
						, thisValidExt
						, thisFileNamePath
						, thisFileNameUniQid
						, thisFileName
						, thisOriginalName
						, thisFileExt
						, thisValidMaxSzie
					;
					if (options.thisFile)  {
						thisFile = options.thisFile;
					}
					if (options.thisObj)  {
						thisObj = options.thisObj;
					}
					if (options.validExt)  {
						thisValidExt = options.validExt;
					}
					if (options.fileName)  {
						thisFileName = options.fileName;
					}
					if (options.fileNamePath)  {
						thisFileNamePath = options.fileNamePath;
					}
					if (options.fileNameUniQid)  {
						thisFileNameUniQid = options.fileNameUniQid;
					}
					if (options.originalName)  {
						thisOriginalName = options.originalName;
					}
					if (options.vlidMaxSzie)  {
						thisValidMaxSzie = options.vlidMaxSzie;
					}
					if (options.fileExt)  {
						thisFileExt = options.fileExt;
					}
					if (!thisFileExt) {
						let OriginalFileName = thisFile.name;
						let thisFileExt = OriginalFileName.split('.').pop();
					}

					if (!thisFile || !thisObj) {
						options.error("文件选择失败,请重试 ~ ");
						return;
					}
					let this_filename = thisFile.name
						, this_file_size = thisFile.size
						, this_file_type = thisFile.type
					;
					if (!this_filename || !this_file_size || this_file_size <= 0) {
						console.log("this_filename  this_file_size ");
						console.log(this_filename);
						console.log(this_file_size);
						options.error("文件选择失败,请重试 ~ ");
						return;
					}
					if (thisValidExt.indexOf(thisFileExt) === -1) {
						options.error("文件不符合类型 ~ ");
						return;
					}
					if (thisValidMaxSzie < this_file_size) {
						options.error("文件超出最大限制 ~ ");
						return;
					}
					if (!thisFileName) {
						thisFileName = thisFileNamePath
							+ this_filename.substring(0, this_filename.lastIndexOf("."))
							+ "_"
							+ thisFileNameUniQid
							+ thisFileExt;
					}
					var error = function(err) {
						if(typeof options.error === 'function'){
							options.error(err);
						}else{
							options.error("上传失败:"+JSON.stringify(err));
						}
					};

					var complete = function(res) {
						if(typeof options.complete === 'function'){
							options.complete(res);
						}else{
							options.complete("上传成功:"+JSON.stringify(res));
						}
					};

					var next = function(response) {
						if(typeof options.next === 'function'){
							console.log("function")
							options.next(response);
						}else{
							var chunks = response.chunks || [];
							var total = response.total;
							// 这里对每个chunk更新进度,并记录已经更新好的避免重复更新,同时对未开始更新的跳过
							for (var i = 0; i < chunks.length; i++) {
								if (chunks[i].percent === 0 || finishedAttr[i]) {
									continue;
								}
								if (compareChunks[i].percent === chunks[i].percent) {
									continue;
								}
								if (chunks[i].percent === 100) {
									finishedAttr[i] = true;
								}
							}
							console.log("上传进度:" + total.percent + "% ");
							compareChunks = chunks;
						}
					};

					observable = qiniu.upload(thisObj, thisFileName, token, putExtra, config);
					var subObject = {next:next,error:error,complete:complete};
					if(typeof callback === 'function'){
						callback(observable,subObject)
					}else{
						observable.subscribe({next:next,error:error,complete:complete});
					}
				});
			});

        }
	})
});

技术要点

1、 引入直接写的七牛云上传组件
       ·  动态先后加载JQ(若之前已加载过JQ可无需再次加载)、加载七牛云JSSDK【并非一定得动态加载,在html中直接引用也可】
       ·   关键代码为:qiniu.upload(file, filename, token, putExtra, config); 务必注意filename 必须与请求token的名称一致,否则会鉴权失败报403;PS: qiniu.upload 会自动判断是否需分片上传,无需过多处理;
2、加载layui组件,需引入直接写的七牛云组件,使用 layui.config 引入
       ·   upload.render 需关闭自动上传(即auto: false)
       ·   上传逻辑使用 upload.render 中的 choose 回调函数,在该函数内进行直接上传或按钮上传

二、 “页面 -> 服务器 -> 七牛云” 上传(简单讲述下)

1、页面上传文件至服务器

若采用layui upload组件可参考以下代码

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>upload模块快速使用</title>
  <link rel="stylesheet" href="/static/build/layui.css" media="all">
</head>
<body>
 
<button type="button" class="layui-btn" id="test1">
  <i class="layui-icon">&#xe67c;</i>上传图片
</button>
 
<script src="/static/build/layui.js"></script>
<script>
layui.use('upload', function(){
  var upload = layui.upload;
   
  //执行实例
  var uploadInst = upload.render({
    elem: '#test1' //绑定元素
    ,url: '/upload/' //上传接口
    ,done: function(res){
      //上传完毕回调
    }
    ,error: function(){
      //请求异常回调
    }
  });
});
</script>
</body>
</html>

2、后端接收文件临时保存至服务器,再使用七牛云提供的SKD进行上传至七牛云

以PHP为例,以下是七牛云 PHP SKD 官方文档 ,其他语言对应的文档可打开后在左边菜单栏选择

https://developer.qiniu.com/kodo/1241/php

关键步骤为

// 引入鉴权类
use Qiniu\Auth;
// 引入上传类
use Qiniu\Storage\UploadManager;
// 需要填写你的 Access Key 和 Secret Key
$accessKey ="your accessKey";
$secretKey = "your secretKey"
$bucket = "your bucket name";
// 构建鉴权对象
$auth = new Auth($accessKey, $secretKey);
// 生成上传 Token
$token = $auth->uploadToken($bucket);
// 上传文件临时保存的路径
$filePath = './php-logo.png';
// 上传至七牛云存储后保存的文件名
$key = 'my-php-logo.png';
// 初始化 UploadManager 对象并进行文件的上传。
$uploadMgr = new UploadManager();
// 调用 UploadManager 的 putFile 方法进行文件的上传。
list($ret, $err) = $uploadMgr->putFile($token, $key, $filePath, null, 'application/octet-stream', true, null, 'v2');
echo "\n====> putFile result: \n";
if ($err !== null) {
    var_dump($err);
} else {
    var_dump($ret);
}

3、上传完成后删除服务器中所上传的原文件(易忽略)

完结~

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐