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 npm install全局安装和本地安装的区别
Jun 05 NodeJs
NodeJS制作爬虫全过程(续)
Dec 22 NodeJs
nodeJS代码实现计算交社保是否合适
Mar 09 NodeJs
基于NodeJS+MongoDB+AngularJS+Bootstrap开发书店案例分析
Jan 12 NodeJs
用nodeJS搭建本地文件服务器的几种方法小结
Mar 16 NodeJs
Nodejs中Express 常用中间件 body-parser 实现解析
May 22 NodeJs
nodejs集成sqlite使用示例
Jun 05 NodeJs
nodejs使用express获取get和post传值及session验证的方法
Nov 09 NodeJs
详解redis在nodejs中的应用
May 02 NodeJs
NodeJs 文件系统操作模块fs使用方法详解
Nov 26 NodeJs
nodejs分离html文件里面的js和css的方法
Apr 09 NodeJs
NodeJS模块Buffer原理及使用方法解析
Nov 11 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
全文搜索和替换
2006/10/09 PHP
ThinkPHP写第一个模块应用
2012/02/20 PHP
浅析PHP开发规范
2018/02/05 PHP
用js查找法实现当前栏目的高亮显示的代码
2007/11/24 Javascript
javascript this用法小结
2008/12/19 Javascript
jquery.pagination.js 无刷新分页实现步骤分享
2012/05/23 Javascript
IE与FireFox的JavaScript兼容问题解决办法
2013/12/31 Javascript
FF IE浏览器修改标签透明度的方法
2014/01/27 Javascript
jQuery中end()方法用法实例
2015/01/08 Javascript
jQuery EasyUI Dialog拖不下来如何解决
2015/09/28 Javascript
详解javascript new的运行机制
2016/01/26 Javascript
几行js代码实现自适应
2017/02/24 Javascript
从零开始做一个pagination分页组件
2017/03/15 Javascript
详解angularJS动态生成的页面中ng-click无效解决办法
2017/06/19 Javascript
ES6 Map结构的应用实例分析
2019/06/26 Javascript
Vue+Spring Boot简单用户登录(附Demo)
2020/11/12 Javascript
vue 封装面包屑组件教程
2020/11/16 Javascript
详解JavaScript 中的批处理和缓存
2020/11/19 Javascript
vue实现拖拽进度条
2021/03/01 Vue.js
Python遍历目录的4种方法实例介绍
2015/04/13 Python
Python lambda表达式用法实例分析
2018/12/25 Python
详解Django定时任务模块设计与实践
2019/07/24 Python
Django结合ajax进行页面实时更新的例子
2019/08/12 Python
HTML5新特性之type=file文件上传功能
2018/02/02 HTML / CSS
十佳青年个人事迹材料
2014/01/28 职场文书
大学社团活动总结
2014/04/26 职场文书
财务人员担保书
2014/05/13 职场文书
小学语文业务学习材料
2014/06/02 职场文书
企业法人授权委托书
2014/09/25 职场文书
工伤事故赔偿协议书(标准)
2014/09/29 职场文书
预备党员党支部意见
2015/06/02 职场文书
《正面管教》读后有感:和善而坚定的旅程
2019/12/19 职场文书
python迷宫问题深度优先遍历实例
2021/06/20 Python
Python实现滑雪小游戏
2021/09/25 Python
SQL基础查询和LINQ集成化查询
2022/01/18 MySQL
MySQL示例讲解数据库约束以及表的设计
2022/06/16 MySQL