Ajax异步文件上传与NodeJS express服务端处理


Posted in NodeJs onApril 01, 2017

为了避免在实现简单的异步文件上传功能时候引入一个第三方库文件的尴尬情形(库文件可能造成多余的开销,拉低应用加载速度,尤其是在引入库文件之后仅使用其中一两个功能的情况下,性价比极低),最近了解了一下文件异步上传的实现原理,顺带看了看进度条、图片预览等功能的实现,做一点简单的整理。

文件上传

HTML结构如下,一个file input和一个button。当点击“上传”按钮的时候,将file input选中的文件上传到服务器。

<input type="file" name="file" id="file" />
<button id="upload">上传</button>

以下是“上传”按钮的点击事件处理器,点击按钮之后通过一个XMLHttpRequest对象来实现发送异步请求。上传的内容为文件,因此还需要用到FormData对象,FormData可以js里面创建表单对象,将file input的文件append到FormData对象中,最后调用XHR对象的send()方法将表单数据发送出去即可。

var file = document.querySelector('#file');
var upload = document.querySelector('#upload');
var xhr = new XMLHttpRequest();

// 点击上传
function uploadFile(event) {
 var formData = new FormData();
 formData.append('test-upload', file.files[0]);
 xhr.onload = uploadSuccess;
 xhr.open('post', '/upload', true);
 xhr.send(formData);
}
// 成功上传
function uploadSuccess(event) {
 if (xhr.readyState === 4) {
 console.log(xhr.responseText);
 }
}

上传进度

在进行文件上传的时候,xhr对象会有一个upload属性,会提供一个progress事件,在相应的事件处理器里面通过事件对象可以知道当前的上传进度,利用这个特点可以很方便地实现进度条或者进度提示。

<input type="file" name="file" id="file" />
<button id="upload">上传</button>
<span id="progress">0%</span>
var progress = document.querySelector('#progress');

// 点击上传
function uploadFile(event) {
 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 setProgress(event) {
 if (event.lengthComputable) {
 var complete = Number.parseInt(event.loaded / event.total * 100);
 progress.innerHTML = complete + '%';
 }
}

图片预览

上传图片的时候可以利用FileReader对象来实现图片预览。FileReader可以异步读取用户电脑上的文件,将file input选中的文件传给FileReader,读取之后取得文件的URL并设置为image元素的src即可让选中的图片文件显示出来。

<input type="file" name="file" id="file" />
<button id="upload">上传</button>
<span id="progress">0</span>
<img id="image" src="" width="200" />
var file = document.querySelector('#file');
file.addEventListener('change', previewImage, false);
// 图片预览
function previewImage(event) {
 var reader = new FileReader();
 reader.onload = function (event) {
 image.src = event.target.result;
 };
 reader.readAsDataURL(event.target.files[0]);
}

服务端处理

使用express搭建一个简单的NodeJS服务端,提供上传文件的接口。express要支持文件上传需要用到中间件,在express官网上有很多介绍。这里我使用的是multer中间件,下面是简单的使用示例。upload.single表示这个接口接受的上传文件数量为1个,'test-upload'限制了上传的表单数据的键为'test-upload'(formData.append(‘test-upload', file.files[0]);)。经过这个中间件处理之后,通过req.file可以访问到文件的相关信息,上传的文件存放在uploads文件夹中。

const upload = require('multer')({ dest: 'uploads/' });
app.post('/upload', upload.single('test-upload'), (req, res) => {
 // 没有附带文件
 if (!req.file) {
 res.json({ ok: false });
 return;
 }
 // 输出文件信息
 console.log('====================================================');
 console.log('fieldname: ' + req.file.fieldname);
 console.log('originalname: ' + req.file.originalname);
 console.log('encoding: ' + req.file.encoding);
 console.log('mimetype: ' + req.file.mimetype);
 console.log('size: ' + (req.file.size / 1024).toFixed(2) + 'KB');
 console.log('destination: ' + req.file.destination);
 console.log('filename: ' + req.file.filename);
 console.log('path: ' + req.file.path);
});

Ajax异步文件上传与NodeJS express服务端处理

由输出可以看到,文件的命名使用一个哈希值表示,并且去除了后缀名,想要保持文件的原有的命名格式,需要再通过fs对文件进行改名。

app.post('/upload', upload.single('test-upload'), (req, res) => {
 // 没有附带文件
 if (!req.file) {
 res.json({ ok: false });
 return;
 }

 // 输出文件信息
 console.log('====================================================');
 console.log('fieldname: ' + req.file.fieldname);
 console.log('originalname: ' + req.file.originalname);
 console.log('encoding: ' + req.file.encoding);
 console.log('mimetype: ' + req.file.mimetype);
 console.log('size: ' + (req.file.size / 1024).toFixed(2) + 'KB');
 console.log('destination: ' + req.file.destination);
 console.log('filename: ' + req.file.filename);
 console.log('path: ' + req.file.path);
 // 重命名文件
 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 });
 }
 });
});

完整代码

ajax异步文件上传、进度显示、图片预览

<input type="file" name="file" id="file" />
<button id="upload">上传</button>
<span id="progress">0</span>
<img id="image" src="" width="200" />
(function () {
 'use strict';

 var file = document.querySelector('#file');
 var upload = document.querySelector('#upload');
 var progress = document.querySelector('#progress');
 var image = document.querySelector('#image');
 var xhr = new XMLHttpRequest();

 upload.addEventListener('click', uploadFile, false);
 file.addEventListener('change', previewImage, false);

 // 点击上传
 function uploadFile(event) {
 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) {
  console.log(xhr.responseText);
 }
 }

 // 进度条
 function setProgress(event) {
 if (event.lengthComputable) {
  var complete = Number.parseInt(event.loaded / event.total * 100);
  progress.innerHTML = complete + '%';
 }
 }

 // 图片预览
 function previewImage(event) {
 var reader = new FileReader();
 reader.onload = function (event) {
  image.src = event.target.result;
 };
 reader.readAsDataURL(event.target.files[0]);
 }
})();

express服务器提供文件上传接口

const express = require('express');
const upload = require('multer')({ dest: 'uploads/' });
const path = require('path');
const fs = require('fs');
const port = 8080;

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('index.html');
});

app.post('/upload', upload.single('test-upload'), (req, res) => {
 // 没有附带文件
 if (!req.file) {
 res.json({ ok: false });
 return;
 }

 // 输出文件信息
 console.log('====================================================');
 console.log('fieldname: ' + req.file.fieldname);
 console.log('originalname: ' + req.file.originalname);
 console.log('encoding: ' + req.file.encoding);
 console.log('mimetype: ' + req.file.mimetype);
 console.log('size: ' + (req.file.size / 1024).toFixed(2) + 'KB');
 console.log('destination: ' + req.file.destination);
 console.log('filename: ' + req.file.filename);
 console.log('path: ' + req.file.path);

 // 重命名文件
 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.listen(port, () => {
 console.log("[Server] localhost:" + port);
});

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持三水点靠木!

NodeJs 相关文章推荐
基于NodeJS的前后端分离的思考与实践(六)Nginx + Node.js + Java 的软件栈部署实践
Sep 26 NodeJs
nodejs中的fiber(纤程)库详解
Mar 24 NodeJs
nodejs加密Crypto的实例代码
Jul 07 NodeJs
用nodejs搭建websocket服务器
Jan 23 NodeJs
NodeJS学习笔记之Module的简介
Mar 24 NodeJs
用Nodejs搭建服务器访问html、css、JS等静态资源文件
Apr 28 NodeJs
NodeJs使用Mysql模块实现事务处理实例
May 31 NodeJs
深入学习nodejs中的async模块的使用方法
Jul 12 NodeJs
nodejs socket服务端和客户端简单通信功能
Sep 14 NodeJs
详解NodeJs开发微信公众号
May 25 NodeJs
NodeJS加密解密及node-rsa加密解密用法详解
Oct 12 NodeJs
NodeJS多种创建WebSocket监听的方式(三种)
Jun 04 NodeJs
3分钟快速搭建nodejs本地服务器方法运行测试html/js
Apr 01 #NodeJs
nodejs使用express创建一个简单web应用
Mar 31 #NodeJs
nodejs实现邮件发送服务实例分享
Mar 29 #NodeJs
NodeJs测试框架Mocha的安装与使用
Mar 28 #NodeJs
NodeJS测试框架mocha入门教程
Mar 28 #NodeJs
nodejs模块nodemailer基本使用-邮件发送示例(支持附件)
Mar 28 #NodeJs
angular2+nodejs实现图片上传功能
Mar 27 #NodeJs
You might like
PHP设计模式之工厂模式与单例模式
2016/09/28 PHP
javascript不可用的问题探究
2013/10/01 Javascript
查询json的数据结构的8种方式简介
2014/03/10 Javascript
让checkbox不选中即将选中的checkbox不选中
2014/07/11 Javascript
用js提交表单解决一个页面有多个提交按钮的问题
2014/09/01 Javascript
js+html5绘制图片到canvas的方法
2015/06/05 Javascript
vue2.0开发入门笔记之.vue文件的生成和使用
2017/09/19 Javascript
React如何避免重渲染
2018/04/10 Javascript
JavaScript实现简单轮播图效果
2018/12/01 Javascript
Easyui 关闭jquery-easui tab标签页前触发事件的解决方法
2019/04/28 jQuery
JavaScript进制转换实现方法解析
2020/01/18 Javascript
vue-cli3项目升级到vue-cli4 的方法总结
2020/03/19 Javascript
python提示No module named images的解决方法
2014/09/29 Python
用Python给文本创立向量空间模型的教程
2015/04/23 Python
Python通过select实现异步IO的方法
2015/06/04 Python
Eclipse和PyDev搭建完美Python开发环境教程(Windows篇)
2016/11/16 Python
Python时间的精准正则匹配方法分析
2017/08/17 Python
Python和Java进行DES加密和解密的实例
2018/01/09 Python
Python常见MongoDB数据库操作实例总结
2018/07/24 Python
在IPython中执行Python程序文件的示例
2018/11/01 Python
在Python中Dataframe通过print输出多行时显示省略号的实例
2018/12/22 Python
python3.x+pyqt5实现主窗口状态栏里(嵌入)显示进度条功能
2019/07/04 Python
简单介绍python封装的基本知识
2019/08/10 Python
springboot配置文件抽离 git管理统 配置中心详解
2019/09/02 Python
python 检查数据中是否有缺失值,删除缺失值的方式
2019/12/02 Python
Python PyQt5模块实现窗口GUI界面代码实例
2020/05/12 Python
浅谈Python描述数据结构之KMP篇
2020/09/06 Python
拉丁舞学习者的自我评价
2013/10/27 职场文书
员工安全生产责任书
2014/07/22 职场文书
会计试用期自我评价
2014/09/19 职场文书
公司领导班子对照检查存在问题整改措施
2014/10/02 职场文书
学生个人评语大全
2015/01/04 职场文书
8个JS的reduce使用实例和reduce操作方式
2021/10/05 Javascript
JVM的类加载器和双亲委派模式你了解吗
2022/03/13 Java/Android
MySQL日期时间函数知识汇总
2022/03/17 MySQL
python热力图实现的完整实例
2022/06/25 Python