下面要做一个ajax上传文件显示进度的操作,文末有演示地址

这里先上代码:

1、前端代码

  upload.html

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>ajax-upload</title>
<link href="https://cdn.bootcss.com/bootstrap/4.0.0/css/bootstrap.css" rel="stylesheet">
<style>
.progress-area {
padding: 20px;
}
</style>
</head>
<body>
<div class="">
<p>文件上传</p>
<div>
<input type="file" name="file" id="file" />
<div>
<p>图片预览(如果上传文件时图片)</p>
<div class="img-preview">
</div>
</div>
<button id="upload">上传</button>
<button id="btn">终止上传</button>
<div class="progress-area">
进度
<div class="progress">
<div class="progress-bar" id="progress" role="progressbar" aria-valuenow="25" aria-valuemin="0" aria-valuemax="100">0%</div>
</div>
<div>
<p id="time"></p>
</div>
</div>
</div>
</div>
<script>
(function () {
'use strict';
var file = document.querySelector('#file');
var upload = document.querySelector('#upload');
var progress = document.querySelector('#progress');
var time = document.querySelector('#time');
var imgPreview = document.querySelector('.img-preview');
var xhr = new XMLHttpRequest();
var loaded = 0, ot = 0, total = 0, oloaded = 0 ;//;
upload.addEventListener('click', uploadFile, false);
file.addEventListener('change', previewImage, false);
// 点击上传
function uploadFile(event) {
if(!file.files[0]) {
alert('请选择文件')
return
}
if(file.files[0].size>10000000) {
alert('文件不得超过10M')
return
}
var formData = new FormData();
formData.append('test-upload', file.files[0]);
xhr.onload = uploadSuccess;
xhr.upload.onprogress = setProgress;
xhr.open('post', '/upload', true);
xhr.send(formData);
}
// 成功上传
function uploadSuccess(event) {
if (xhr.readyState === 4 && xhr.status === 200) {
setTimeout(()=> {
alert('上传成功')
window.location.reload();
},1000)
}
}
// 进度条
function setProgress(event) {
// event.total是需要传输的总字节,event.loaded是已经传输的字节。如果event.lengthComputable不为真,则event.total等于0
if (event.lengthComputable) {//
loaded = event.loaded
total = event.total
var complete = (event.loaded / event.total * 100).toFixed(1);
progress.innerHTML = Math.round(complete) + "%";
progress.style.width = complete + '%';
}
var time = document.getElementById("time");
var nt = new Date().getTime();//获取当前时间
var pertime = (nt-ot)/1000; //计算出上次调用该方法时到现在的时间差,单位为s
ot = new Date().getTime(); //重新赋值时间,用于下次计算
var perload = event.loaded - oloaded; //计算该分段上传的文件大小,单位b
oloaded = event.loaded;//重新赋值已上传文件大小,用以下次计算
//上传速度计算
var speed = perload/pertime;//单位b/s
var bspeed = speed;
var units = 'b/s';//单位名称
if(speed/1024>1){
speed = speed/1024;
units = 'k/s';
}
if(speed/1024>1){
speed = speed/1024;
units = 'M/s';
}
speed = speed.toFixed(1);
//剩余时间
var resttime = ((event.total-event.loaded)/bspeed).toFixed(1);
time.innerHTML = '传输速度:'+speed+units+',剩余时间:'+resttime+'s';
// if(bspeed==0) time.innerHTML = '上传已取消';
}
// 图片预览
function previewImage(event) {
imgPreview.innerHTML = ''
// 每次重新选择文件的时候,都会去除上次选择产生的img标签
var isImg = (event.target.files[0].type).indexOf('image/') > -1;
if(isImg) {
// 如果是图片 就解析图片预览
var img = document.createElement('img')
imgPreview.appendChild(img)
var reader = new FileReader();
reader.onload = function (event) {
img.src = event.target.result;
img.width = '200'
};
reader.readAsDataURL(event.target.files[0]);
} else {
// imgPreview.appendChild('<img src=""/>')
// 可以为非图片文件选择一个默认的文件logo
}
}
//++++++++++++++++++++++++++++++++++++++++++++
xhr.onloadstart = function(){
console.log("上传开始");
}
btn.onclick = function(){
xhr.abort();
console.log("上传被终止.");
progress.style.width = 0 + '%';
progress.innerHTML = 0 + '%';
// 这里调用取消上传的代码
};
xhr.ontimeout = function(){
console.log('上传超时.');
}
// xhr.timeout = 50000; // 默认为0 没有时间限制
// xhr.onabort = function(){
// console.log("The transfer has been canceled by the user.");
// }
xhr.onerror = function(){
console.log("上传错误,可能是断网了,也可能是断请求服务了."); // 这里存在异步传输问题
return
}
xhr.onloadend = function(){
console.log("请求结束"); // 发送上传的请求,至于有没有上传成功,不清楚,可能失败 成功,这里只是请求结束了
}
// +++++++++++++++++++++++++++++++++++++++++++
})();
</script>
</body>
</html>

  

2、后端接口(nodejs)

  app.js

   const express = require('express');
const upload = require('multer')({ dest: 'uploads/' });
const path = require('path');
const fs = require('fs');
const port = ; let app = express(); app.set('port', port);
// index.html, index.js放在static文件夹中
app.use(express.static(path.join(__dirname, 'static'))); // app.get('/', (req, res) => {
// res.redirect('upload2.html');
// }); // 路由/ajax-upload 就回渲染 upload.html 页面
app.get('/ajax-upload', function(req, res){
res.sendFile('upload.html', { root: __dirname });
});
app.post('/upload', upload.single('test-upload'), (req, res) => {
// 没有附带文件
if (!req.file) {
res.json({ ok: false });
return;
}// 重命名文件
let oldPath = path.join(__dirname, req.file.path);
let newPath = path.join(__dirname, 'uploads/' + req.file.originalname);
fs.rename(oldPath, newPath, (err) => {
if (err) {
res.json({ ok: false });
console.log(err);
} else {
res.json({ ok: true });
}
});
});
// 这里还没有做上传取消 删除文件的操作
app.get('/abort', upload.single('test-upload'), (req, res)=>{
console.log(req, 'abort') // 删除刚才上传的文件
}) app.listen(port, () => {
console.log("[Server] localhost:" + port);
});

注:接口使用

  • 环境:nodejs
  • 项目结构 
    • static(图片如下)

      • app.js
      • upload.html
      • uploads
  • 框架包express: npm install express –save
  • 上传包multer :npm install multer –save
  • 启动: node app.js

以上代码可以直接复制运行:

3、解释

3.1.前端

上传简述: 前端的选择文件,掉后端上传接口,利用ajax技术将文件上传的服务器。前端要知道后端是否上传成功或者状态,就得需要后端返回给我们的状态,最简单的就是上传成功或者失败,再者需要知道进度的就要利用xhr 的进程方法了。
  • ajax 的原理就是利用浏览器的XMLHttRequest 这个对象,因为IE浏览器不是这对象,如果要兼容的话 ,可以封装一个XHR 对象,代码可以看后面的附属部分:
  • XMLHttRequest 对象有多个方法,监控ajax 上传的进度主要利用这些属性,来达到我们的目标
  • 下面是部分属性(方法)介绍,这里是MDN的介绍 xhr 
    • onloadstart 获取开始
    • onprogress 数据传输进行中
    • onabort 获取操作终止
    • onerror 获取失败
    • onload 获取成功
    • ontimeout 获取操作在用户规定的时间内未完成
    • onloadend 获取完成(不论成功与否)

* 对xhr.upload.onprogress的解释

这里监控进度主要看这里的属性值的变化,如下图 

具体的代码片段解释可以查看我上面的代码注释

3.2.后端

这里使用的nodejs的一个接口,之前我的用的是将前端预览图片的base64的字符串传到后端,然后后端解析这个base64的字符串,生成图片,保存在磁盘中,但是会出现问题,进度不太好显示,所以查了相关资料,借用了这里的写法,直接上传文 件;还有一个就是multer包,利用这个进行上传。

这里项目也是一个后端渲染的方式,将页面渲染好,然后发送给前端。

4、附属代码:

4.1.兼容的ajax代码段

function XMLHttpRequest(){
if (typeof XMLHttpRequest != "undefined"){
return new XMLHttpRequest();
} else if (typeof ActiveXObject != "undefined"){
if (typeof arguments.callee.activeXString != "string"){
var versions = [ "MSXML2.XMLHttp.6.0", "MSXML2.XMLHttp.3.0",
"MSXML2.XMLHttp"],
i, len;
for (i=0,len=versions.length; i < len; i++){
try {
new ActiveXObject(versions[i]);
arguments.callee.activeXString = versions[i];
break;
} catch (ex){
//跳过
}
}
}
return new ActiveXObject(arguments.callee.activeXString);
} else {
throw new Error("No XHR object available.");
}
}

4.2.nodejs 处理base64字符串

node包:formidable 
imgBase64Arr: 前端传值过来的 base64字符串的多张图片的数组

for(var i = 0;i<imgBase64Arr.length;i++) {
var imgname = util.randomStr(); // 随机字符串的方法
imgname = 'assets/img/'+ imgname + '.png';
var base64 = imgBase64Arr[i].replace(/^data:image\/\w+;base64,/, "");
//去掉图片base64码前面部分data:image/png;base64
var dataBuffer = new Buffer(base64, 'base64'); //把base64码转成buffer对象,
console.log('dataBuffer是否是Buffer对象:'+Buffer.isBuffer(dataBuffer));
fs.writeFile(imgname,dataBuffer,function(err){//用fs写入文件
if(err){
console.log(err);
}else{
console.log('图片上传成功!');
}
})
newimgarr.push(imgname.replace("assets",""));
}

github地址:https://github.com/adouwt/ajax-upload

前端上传这块已经封装了一个基于vue的插件,

GitHub地址:https://github.com/adouwt/vue-upload

npm 官网上的数据显示已经有一定的下载量,欢迎大家学习使用,有bug,及时告知于我

https://www.npmjs.com/package/vue-ajax-upload

如有错误,敬请指出!

这里提供了一个demo演示如下, ps:个人服务器存储较小,只是用来展示,后端是个小白,没有做一些文件过滤和后端文件风险校验,大神请绕行哈,还有请手下留情哈!

推荐文章:

  https://www.cnblogs.com/tianyuchen/p/5594641.html
戳我

ajax上传文件显示进度的更多相关文章

  1. ajax上传文件及进度显示

    之前在博文:原生ajax写法就提及过ajax2.0与1.0的差别是多了FormData和利用FormData文件上传(当然还有跨域,但不是本文的重点). 那么具体怎么样实现ajax上传文件呢? 一般来 ...

  2. HTML5上传文件显示进度

    下面我们使用Html 5的新特性file api实现上传文件,并显示上传文件进度百分比.意图是这样的,当选择文件时,显示当前文件信息.这里我们是结合Asp.net MVC做为服务端,您也可以是其它的服 ...

  3. axios+Vue上传文件显示进度

    一,前言 最近在用Vue,然后上传文件时需要显示进度,于是网上搜了一下,经过自己实测终于也弄明白了 二,效果 三,代码 HTML代码 <div id="app"> &l ...

  4. jQuery上传文件显示进度条

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <script sr ...

  5. ASP.NET Jquery+ajax上传文件(带进度条)

    效果图 支持ie6+,chrome,ie6中文文件名会显示乱码. 上传时候会显示进度条. 需要jquery.uploadify.js插件,稍后会给出下载 前台代码 <%@ Page Langua ...

  6. 【ASP.NET MVC】HTML5+MVC上传文件显示进度

    head> <title>Index</title> <style type="text/css"> #statusBorder { po ...

  7. Ajax上传文件进度条显示

    要实现进度条的显示,就要知道两个参数,上传的大小和总文件的大小 html5提供了一个上传过程事件,在上传过程中不断触发,然后用已上传的大 小/总大小,计算上传的百分比,然后用这个百分比控制div框的显 ...

  8. Asp.Net实现无刷新文件上传并显示进度条(非服务器控件实现)(转)

    Asp.Net实现无刷新文件上传并显示进度条(非服务器控件实现) 相信通过Asp.Net的服务器控件上传文件在简单不过了,通过AjaxToolkit控件实现上传进度也不是什么难事,为什么还要自己辛辛苦 ...

  9. 【原创】用JAVA实现大文件上传及显示进度信息

    用JAVA实现大文件上传及显示进度信息 ---解析HTTP MultiPart协议 (本文提供全部源码下载,请访问 https://github.com/grayprince/UploadBigFil ...

随机推荐

  1. JS获取登录者IP和登录城市

    登录城市:<sp class="cy"></sp><br /> 管理员个数:<font color=</strong>< ...

  2. ORM简介

    ORM就是object relational mapping,对象关系映射. 将关系型数据库转化为对象来进行处理. 数据表就是一个类,表的一行就是一个对象,一行的每个字段就是属性. 忽然想到了在MVC ...

  3. [wiki] Unix like

    1. Unix的发展历史 2. 纵向的图 3. 来源: https://zh.wikipedia.org/wiki/类Unix系统 4. 中文版   数种“类UNIX操作系统”的相互关系图 类Unix ...

  4. Lodop提示BarCode Type(ena13)Invalid!

    前段时间遇到过一个奇怪的问题,就是代码里本身都是ENA13大写,却提示条码类型无效,而且进入打印设计后,选中打印项,条码类型变成了code39,但是实际是还有条码类型参数都是正确的,代码看上去没有问题 ...

  5. 解决Windows10中Virtualbox安装虚拟机没有64位选项

    今天想在Windows 10系统安装完Virtualbox虚拟机,然后打算装一个CENTOS系统,但是选择安装系统的时候竟然没有64位操作系统的选项,经过一阵Google,终于解决了,在这里盘点一下出 ...

  6. 【转】Java 线程池

    什么是线程池? 线程池是指在初始化一个多线程应用程序过程中创建一个线程集合,然后在需要执行新的任务时重用这些线程而不是新建一个线程.线程池中线程的数量通常完全取决于可用内存数量和应用程序的需求.然而, ...

  7. TCPDUMP 使用教程

    TCPDUMP 命令使用简介 简单介绍 tcpdump 是一款强大的网络抓包工具,运行在 Linux 平台上.熟悉 tcpdump 的使用能够帮助你分析.调试网络数据. 要想很好地掌握 tcpdump ...

  8. TensorFlow 辨异 —— tf.placeholder 与 tf.Variable

    https://blog.csdn.net/lanchunhui/article/details/61712830 https://www.cnblogs.com/silence-tommy/p/70 ...

  9. pandas的读写

    import as pd import numpy as np import matplotlib.pyplot as plt #df.to_excel('C:Users/history/Deskto ...

  10. 商务电话思维图(XMind für Geschäftliche Telefongespräche)

    在和德国人打交道时,经常会遇到打电话的情景,应该怎么应对呢?不用担心,记住下面这个导图,轻松搞定德语电话的常用句型. 最后,按照惯例,来张美景.人越是上了年纪,就活的越是小心.但无论外界怎么样,请保持 ...