七牛图片上传
-- 网络请求实战
课程概要
本课程主要讲解图片上传案例,在日常开发中,我们会涉及到图片的上传,大部分对静态资源的管理都托管在云存储,本节课程将为大家实战图片是如何与云存储服务以及本服务器交互。涉及到请求和异步的知识点有 XJAX、XHR、Fetch、AJAX、AXIOS 以及 callback、Promise、asyncAwait。通过本课程,完成一次七牛图片的存储交互,大家将网络请求、异步的原理和开发有更深入的了解。
主要包含以下功能:
- 选择图片
- 获取七牛令牌
- 把图片上传到七牛云存储
- 把七牛云存储的图片地址告诉服务器
相关原理
步骤:
- 请求获取 token
- 把图片上传到七牛
- 获取图片的七牛地址上传
知识点
本课程涉及到的主要知识点有:
- AJAX
传统的Web应用允许用户端填写表单(form),当提交表单时就向网页服务器发送一个请求。服务器接收并处理传来的表单,然后送回一个新的网页,但这个做法浪费了许多带宽,因为在前后两个页面中的大部分HTML码往往是相同的。由于每次应用的沟通都需要向服务器发送请求,应用的回应时间依赖于服务器的回应时间。这导致了用户界面的回应比本机应用慢得多。
AJAX即“Asynchronous JavaScript and XML”(异步的JavaScript与XML技术),指的是一套综合了多项技术的浏览器端网页开发技术。Ajax的概念由杰西·詹姆士·贾瑞特所提出。
Ajax 技术的核心是 XMLHttpRequest 对象(简称 XHR),这是由微软首先引入的一个特性,其他 浏览器提供商后来都提供了相同的实现。在 XHR 出现之前,Ajax 式的通信必须借助一些 hack 手段来实 现,大多数是使用隐藏的框架或内嵌框架。XHR 为向服务器发送请求和解析服务器响应提供了流畅的 接口。能够以异步方式从服务器取得更多信息,意味着用户单击后,可以不必刷新页面也能取得新数据。 也就是说,可以使用 XHR 对象取得新数据,然后再通过 DOM 将新数据插入到页面中。另外,虽然名 字中包含 XML 的成分,但 Ajax 通信与数据格式无关。这种技术就是无须刷新页面即可从服务器取得数 据,但不一定是 XML 数据。
- XMLHttpRequest
XMLHttpRequest 是 HTML 原生的网络请求对象, XHR
function _XHR(method, url, datas,success) {
let xhr = new XMLHttpRequest();
xhr.open(method, url, true);
let formData = new FormData();
for(let key in datas){
formData.append(key, datas[key]);
}
xhr.onerror = function(xhr, status, text) {
console.log(xhr, status, text);
};
xhr.onreadystatechange = function(response) {
if (xhr.readyState == 4 && xhr.status == 200 && xhr.responseText != "") {
typeof success === 'function' && success(JSON.parse(xhr.response))
} else if (xhr.status != 200 && xhr.responseText) {
console.log(xhr, xhr.status, xhr.responseText);
}
};
xhr.send(formData);
}
const URL = 'https://www.jevescript.com/api/qiniu-token';
_XHR('GET',URL,{},(res)=>{
console.log(res)
})
- Fetch
Fetch API 提供了一个获取资源的接口(包括跨域)。任何使用过 XMLHttpRequest 的人都能轻松上手,但新的API提供了更强大和灵活的功能集。
const URL = 'https://www.jevescript.com/api/qiniu-token';
fetch(URL)
.then(function(response) {
return response.json();
})
.then(function(myJson) {
console.log(myJson);
});
- jQuery $.ajax
jQuery 执行一个异步的HTTP(Ajax)的请求。$.ajax 使用示例:
HTML 需要引用 jQuery 依赖
<script src="https://lib.baomitu.com/jquery/3.3.1/jquery.min.js"></script>
$.ajax({
type: 'GET',
url: URL,
data: {},
success: ( res )=>{
console.log(res)
},
error: (err) => {
console.log(err)
}
})
- axios
Axios 是一个基于 promise 的 HTTP 库,可以用在浏览器和 node.js 中。axios 使用示例:
HTML 需要引用 axios 依赖
<script src="https://lib.baomitu.com/axios/0.19.0-beta.1/axios.js"></script>
const URL = 'https://www.jevescript.com/api/qiniu-token';
axios.get(URL).then( res => {
console.log(res.data)
})
- 异步处理
由于 XHR 是一个异步的机制,在处理请求的时候,需要等待服务端返回才能拿到数据。有时候我们需要从服务端返回拿到数据,再用这个数据去请求服务端,再用返回的数据去处理其他逻辑。当嵌套够深的时候,就会陷入一个 callbackhell(回调地狱)。在处理异步事件中,主要有以下几种方法:
- callback
- Promise
- async/await 在此我们统一用 setTimeout 来模拟 xhr 的异步数据返回。
callback
const xhr1 = function(cb){
setTimeout(()=>{
const data = 1;
typeof cb === 'function' && cb(data)
},2000)
}
const xhr2 = function(cb){
setTimeout(()=>{
const data = 2;
typeof cb === 'function' && cb(data)
},2000)
}
const xhr3 = function(cb){
setTimeout(()=>{
const data = 3;
typeof cb === 'function' && cb(data)
},2000)
}
// 执行
xhr1((data1)=>{
console.log(data1)
xhr2((data2)=>{
console.log(data2)
xhr3((data3)=>{
console.log(data3)
})
})
})
Promise
const xhr1 = function(){
return new Promise((resolve,reject)=>{
setTimeout(()=>{
const data = 1;
resolve(data)
},2000)
})
}
const xhr2 = function(){
return new Promise((resolve,reject)=>{
setTimeout(()=>{
const data = 2;
resolve(data)
},2000)
})
}
const xhr3 = function(){
return new Promise((resolve,reject)=>{
setTimeout(()=>{
const data = 3;
resolve(data)
},2000)
})
}
// 执行
xhr1().then((data1)=>{
console.log(data1);
return xhr2();
}).then((data2)=>{
console.log(data2);
return xhr3();
}).then((data3)=>{
console.log(data3)
}).catch( e => {
console.log(e)
})
// 并行执行
Promise.all([xhr1(),xhr2(),xhr3()]).then((data)=>{
console.log(data)
}).catch( e => {
console.log(e)
})
async/await
const xhr1 = function(){
return new Promise((resolve,reject)=>{
setTimeout(()=>{
const data = 1;
resolve(data)
},2000)
})
}
const xhr2 = function(){
return new Promise((resolve,reject)=>{
setTimeout(()=>{
const data = 2;
resolve(data)
},2000)
})
}
const xhr3 = function(){
return new Promise((resolve,reject)=>{
setTimeout(()=>{
const data = 3;
resolve(data)
},2000)
})
}
async function xhr(){
const data1 = await xhr1();
console.log(data1);
const data2 = await xhr2();
console.log(data2);
const data3 = await xhr3();
console.log(data1,data2,data3);
}
xhr();
实现步骤
- HTML & CSS
- 构建单例对象PAGE
- XHR 上传
- Axios 上传
- Fetch Async/Await 上传
HTML & CSS
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>uploadImage</title>
</head>
<body>
<input id="upload" type="file" name="fulAvatar">
<button id="submit">上传</button>
<script src="https://lib.baomitu.com/axios/0.19.0-beta.1/axios.min.js"></script>
<script type="text/javascript">
</script>
</body>
</html>
构建单例对象PAGE
- 定义 PAGE
- 在 PAGE.data 中存放上传 API
// 1. 定义 PAGE
const PAGE = {
// 2. 在 PAGE.data 中存放上传 API
data: {
TOKEN_API:'https://www.jevescript.com/api/qiniu-token',
QINIU_API: 'http://upload-z2.qiniup.com',
UPLOAD_API :'https://www.jevescript.com/api/image-upload'
},
init: function() {
this.bind();
},
bind: function() {
}
}
PAGE.init();
XHR 上传
- 封装 PAGE._XHR 公共请求方法
- 绑定点击按钮事件
- 点击按钮判断是否有选择图片
- 获取 token
- 上传图片
- 上传图片地址
const PAGE = {
data: {
TOKEN_API:'https://www.jevescript.com/api/qiniu-token',
QINIU_API: 'http://upload-z2.qiniup.com',
UPLOAD_API :'https://www.jevescript.com/api/image-upload'
},
init: function() {
this.bind();
},
bind: function() {
// 2. 绑定点击按钮事件
let button = document.getElementById('submit');
button.addEventListener('click',this.XHRuploadImage);
},
XHRuploadImage: function() {
// 3. 点击按钮判断是否有选择图片
let files = document.getElementById('upload').files;
let file = files[0];
if(!file){
console.log('缺少文件');
return
}
// 4. 获取 token
let domain;
PAGE._XHR('GET', PAGE.data.TOKEN_API ,{},(res)=>{
let token = res.data.uptoken;
domain = res.data.domain;
let key = Date.now() + '_' + file.name;
let formData = {};
formData['file'] = file;
formData['key'] = key;
formData['fname'] = file.name;
formData['token'] = token;
formData['x.name'] = file.name;
formData['form-data'] = true;
// 5. 上传图片
PAGE._XHR('POST', PAGE.data.QINIU_API, formData, (res) => {
let image_url = domain +'/' + key;
// 6. 上传图片地址
PAGE._XHR('POST', PAGE.data.UPLOAD_API, { image_url }, (res) => {
if(res.code === 200 ){
console.log(image_url);
alert('提交成功!')
}else{
alert('提交失败!')
}
})
})
})
},
//1. 封装 PAGE.\_XHR 公共请求方法
_XHR: function(method,url,datas,success,progress,error,csrf) {
let xhr = new XMLHttpRequest();
xhr.open(method, url, true);
if(csrf){
xhr.withCredentials = true;
xhr.setRequestHeader('X-CSRF-TOKEN',csrf);
}
let formData;
if(datas['form-data']){
formData = new FormData();
for(let key in datas){
formData.append(key, datas[key]);
}
datas = formData;
}else{
formData = JSON.stringify(datas);
xhr.setRequestHeader('content-type', 'application/json')
}
xhr.upload.onprogress = function (event) {
typeof progress === 'function' && progress(event);
};
xhr.onerror = function(xhr, status, text) {
typeof error === 'function' && error(xhr, status, text);
};
xhr.onreadystatechange = function(response) {
if (xhr.readyState == 4 && xhr.status == 200 && xhr.responseText != "") {
typeof success === 'function' && success(JSON.parse(xhr.response))
} else if (xhr.status != 200 && xhr.responseText) {
typeof error === 'function' && error(xhr, xhr.status, xhr.responseText);
}
};
xhr.send(formData);
}
}
PAGE.init();
Axios 上传
- 引入 axios
- 绑定点击按钮事件
- 点击按钮判断是否有选择图片
- 获取 token
- 上传图片
- 上传图片地址
<!-- 1. 引入 axios -->
<script src="https://lib.baomitu.com/axios/0.19.0-beta.1/axios.min.js"></script>
const PAGE = {
data: {
TOKEN_API:'https://www.jevescript.com/api/qiniu-token',
QINIU_API: 'http://upload-z2.qiniup.com',
UPLOAD_API :'https://www.jevescript.com/api/image-upload'
},
init: function() {
this.bind();
},
bind: function() {
let button = document.getElementById('submit');
// button.addEventListener('click',this.XHRuploadImage);
// 2. 绑定点击按钮事件
button.addEventListener('click',this.AXIOSuploadImage);
},
AXIOSuploadImage: function() {
// 3. 点击按钮判断是否有选择图片
let files = document.getElementById('upload').files;
let file = files[0];
if(!file){
console.log('缺少文件');
return
}
// 4. 获取 token
let domain;
let key = Date.now() + '_' + file.name;
axios.get(PAGE.data.TOKEN_API)
.then( res => {
let token = res.data.data.uptoken;
domain = res.data.data.domain;
return token
})
.then( token => {
let formData = new FormData();
formData.append('file', file);
formData.append('key', key);
formData.append('fname', file.name);
formData.append('token', token);
formData.append('x.name', file.name);
// 5. 上传图片
return axios.post(PAGE.data.QINIU_API, formData, {
headers: {
'Content-Type': 'multiple/form-data'
}
})
})
.then( res => {
let image_url = domain +'/' + key;
// 6. 上传图片地址
return axios.post(PAGE.data.UPLOAD_API,{ image_url })
})
.then( res => {
if(res.data.code === 200 ){
alert('提交成功!')
}else{
alert('提交失败!')
}
})
},
...
}
Fetch Async/Await 上传
- 绑定点击按钮事件
- 点击按钮判断是否有选择图片
- 获取 token
- 上传图片
- 上传图片地址
const PAGE = {
data: {
TOKEN_API:'https://www.jevescript.com/api/qiniu-token',
QINIU_API: 'http://upload-z2.qiniup.com',
UPLOAD_API :'https://www.jevescript.com/api/image-upload'
},
init: function() {
this.bind();
},
bind: function() {
let button = document.getElementById('submit');
// button.addEventListener('click',this.XHRuploadImage);
// button.addEventListener('click',this.AXIOSuploadImage);
// 1. 绑定点击按钮事件
button.addEventListener('click',this.FetchuploadImage);
},
FetchuploadImage: async function() {
// 2. 点击按钮判断是否有选择图片
let files = document.getElementById('upload').files;
let file = files[0];
if(!file){
console.log('缺少文件');
return
}
// 3. 获取 token
let tokenFetch = await fetch(PAGE.data.TOKEN_API).then((response) => response.json());
let token = tokenFetch.data.uptoken;
let domain = tokenFetch.data.domain;
let key = Date.now() + '_' + file.name;
let formData = new FormData();
formData.append('file', file);
formData.append('key', key);
formData.append('fname', file.name);
formData.append('token', token);
formData.append('x.name', file.name);
// 4. 上传图片
let qiniuFetch = await fetch(PAGE.data.QINIU_API,{
method: 'POST',
body: formData
}).then(response => response.json())
let image_url = domain +'/' + key;
// 5. 上传图片地址
let uploadFetch = await fetch(PAGE.data.UPLOAD_API,{
method: 'POST',
body: JSON.stringify({ image_url }),
headers: {
'content-type': 'application/json'
},
}).then(response => response.json())
if(uploadFetch.code === 200 ){
alert('提交成功!')
}else{
alert('提交失败!')
}
},
...
}