Ajax 编程基础——FormData——视频上传(二)

模板引擎

node.js 基础学习时就已经接触过模板引擎的概念,但那是为了在服务器端对数据和 HTML 结构进行拼接并响应给客户端。现在使用的是 ajax 技术,因此是需要在客户端对数据和模板进行拼接并直接更新在页面中。

原理是相同的原理,因此使用的步骤也大致相同。既然在服务器端使用的是 art-template 模板引擎,在客户端我们也选择这款模板引擎。因为它们的模板语法是一摸一样的,这就节省了再重新学习其他模板引擎的语法的时间精力。

1、art-template 官网 下载 art-template 模板引擎库文件并在 HTML 页面中引入库文件。

<script src="/js/template-web.js"></script>

2、准备 art-template 模板,art-template 模板是 html 文件中的一些代码片段,使用 script 标签包裹。使用 id 属性标识不同的模板,为了提高模板代码的可读性,可以为其设置 type 属性。

<script id="tpl" type="text/html">
	<div class="box"></div>
</script>

3、告诉模板引擎需要拼接哪个模板和数据

<script>
	var html = template('tpl', {userName: 'tkop', age: 18});
</script>

4、将拼接好的 html 字符串更新至页面中

// 将拼接好的字符串作为dom元素的内容
document.getElementById('container').innerHTML = html;

5、使用模板语法对数据和模板进行拼接操作

<script id="tpl" type="text/html">
	<div class="box">{{ userName }}</div>
</script>

有关 art-template 模板语法在 node.js 基础部分学习 express 框架时已经有所了解在此不重复笔记,笔记链接:Express 框架基础

FormData 对象

在我们需要发送的 ajax 请求需要携带大量的表单信息时, 逐个获取表单控件元素并将它们的值组合成需要的参数格式显然比较繁琐。前面学习过一个有关的表单方法 serializeArray() 可以方便我们获取表单控件中的数据,但是对于需求的实现还是不够理想。此时 ES6 中提供的 FormData 对象可以完美解决我们遇到的问题,但是 IE10 以下的浏览器不支持。

FormData 对象是一类用于模拟 HTML 表单,相当于将 HTML 表单映射成为表单对象(通过 js 使用键值对的形式表示表单控件的 name 和 value)。它自动将表单对象中的数据拼接成请求参数的格式。还具有异步上传二进制文件的功能。

FormData 对象的使用

1、准备 HTML 表单

<form id="form">
	<input type="text" name="username" />
	<input type="password" name="password" />
	<input type="button" />
</form>

不需要为表单设置请求方式和请求地址,也不需要提交按钮(当然可以设置一个 button 表单控件并为其绑定点击事件,点击后发送 ajax 请求提交表单),因为此时需要发送的是 ajax 请求。

2、将 HTML 表单转换为 formData 对象

利用表单元素和 FormData 构造函数创建表单对象。当然也可以创建一个空的表单对象,再根据需要使用表单对象的 set() 和 append() 方法为其添加数据。

var form = focument.getElementById('form');
var formData1 = new FormData(form);

// 也可以创建空的表单对象、
var formData2 = new FormData();

3、提交表单对象

提交表单时 ajax 请求不需要设置请求头(FormData 自带请求头),所以不要存在为什么发送 ajax 请求前不去设置请求头信息的疑惑。例如以前我们发送请求前需要指明参数格式 xhr.setRequestHeader = '参数格式' ,现在不需要设置,直接发送即可。

xhr.send(formData1);

注意:

  • FormData 对象不能直接用于 get 请求,因为 get 请求方式的请求参数只能放在请求地址的后面,对象需要被传递到 send 方法中。
  • 服务器端 bodyParser 模块无法解析 formData 对象表单数据,我们需要使用 formidable 模块进行解析。
  • 在表单中并没有设置属性 enctype=“multipart/form-data” 来明确规定使用二进制形式表示表单数据,但是在后面可以直接进行二进制文件的上传功能和使用 formidable 模块解析。这说明 formData 对象在表示表单数据时使用的就是二进制形式。
  • 只有 bodyParser 模块在解析数据时需要配置以什么格式解析参数,使用 formData 模块不需要。这一点在以前的学习中没有去明确,现在补上。

在此明确一些题外知识:

  1. 表单传统方式提交时请求头信息中表示参数格式的字段内容为 Content-Type: application/x-www-form-urlencoded
  2. Formdate 对象自带的请求头信息中表示参数格式的字段内容为Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryxCZ0XYGrKJwqGsJw 。其数据在请求体中具体的形式与以前接触过的哪两种类型差异很大,如下图所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EsA9CcN2-1618660512078)(https://z3.ax1x.com/2021/04/17/c5iGND.png)]

FormData 对象具有的方法

// 1、获取表单对象中属性的值formData.get('key');
formData1.get('name');
// 2、设置表单对象中的值formData.set('key', 'value');
formData1.set('name', '扬尘');
// 3、删除表单对象中属性的值formData.delete('key');
formData1.delete('age');
// 4、向表单对象中追加属性值formData.append('key', 'value');
formData1.append('hobbies', '敲代码');

注意:set 方法和 append 方法的区别是。在属性名已经存在的情况下,set 会覆盖已有的键名的值,append 会保留两个值(但是服务器使用的是最后的那个参数值)。

二进制文件上传

在利用 FormData 对象实现二进制文件上传功能前首先了解以下几个知识。

  1. 文件选择控件如果设置 multiple 属性是可以一次选择多个文件的,这些文件会保存在控件元素对象的 files 属性中。files 属性是保存文件的一个数组(无论用户选择几个文件)。

  2. ajax 请求对象 xhr 有一个 upload(上传)对象,上传相关的事件都会存储在这个对象中。

  3. 文件上传过程中会持续触发 upload 对象下的 progress(进度)事件,并且该事件的事件对象中存储了上传文件的总大小和已上传数据大小。事件对象的 total、loaded 属性分别保存了文件总大小和已上传数据的大小。

图片预览功能是在我们将图片上传至服务器后,服务器通常都会将图片地址作为响应数据传递到客户端。客户端可以从响应数据中获取图片地址,然后将图片显示在页面中(与前面还未上传前的预览不同)。未上传前的预览当时使用的是 js 内置的 FileReader 对象(存在兼容性问题)在客户端直接读取和显示图片。

<body>
    <form action="">
        <input type="file" name="file" id="file" multiple>
        <input type="button" value="上传" id="btn">
        <div id="prev">
            <!-- <video src="" muted="muted" autoplay="autoplay" loop="loop"></video> -->
        </div>
        <div class="progress" id="progress" style='display:none;'>
            <div class="progress-bar" style="width: 0%;" id="progress-bar">0%</div>
        </div>
    </form>
</body>

<script>
    // 各dom元素的获取
    var file = document.getElementById('file');
    var btn = document.getElementById('btn');
    var progressBar = document.getElementById('progress-bar');
    var progress = document.getElementById('progress');
    var prev = document.getElementById('prev');
    // 上传按钮点击事件
    btn.onclick = function() {
    	// 需要先将上次的上传预览清除掉
        prev.innerHTML = '';
        var xhr = new XMLHttpRequest();
        xhr.open('post', 'http://localhost:3000/formdata');
        // 创建表单对象并将各二进制文件追加至表单对象中
        var formdata = new FormData();
        for (let i = 0; i < file.files.length; i++) {
        	// 这里的文件名称是自定义的
            formdata.append('video' + i, file.files[i]);
        }
        // 进度条功能实现
        xhr.upload.onprogress = function(e) {
            progress.style.display = 'block';
            var result = Math.round((e.loaded / e.total) * 100) + '%';
            progressBar.style.width = result;
            progressBar.innerHTML = result;
        }

        // 上传成功在获取到响应数据后预览文件(视频)
        xhr.onload = function() {
            if (xhr.status == 200) {
                var result = JSON.parse(xhr.responseText);
                // 已经上传完成可以将进度条隐藏
                progress.style.display = 'none';
                // 服务器端响应的数据 result={vide0path:'文件0路径', vide1path:'文件1路径', ...}
                for (var k in result) {
                    var video = document.createElement('video');
                    video.src = result[k];
                    video.muted = "muted";
                    video.autoplay = "autoplay";
                    video.loop = "loop";
                    prev.appendChild(video);
                }
            }
        }
        xhr.send(formdata);
    }
</script>

在这里插入图片描述如上图是上传多个文件(一个也一样)后在服务器端的 files 对象的内容,图中的内容可以帮我们捋清获取各个文件保存路径并响应给客户端的思路。

// 服务器端的代码
const express = require('express');
const formidable = require('formidable');
const path = require('path');
const app = express();
app.use(express.static(path.join(__dirname, 'public')));
// 实现文件上传的路由
app.post('/formdata', (req, res) => {
    const form = new formidable.IncomingForm();
    // 配置文件保存路径
    form.uploadDir = path.join(__dirname, 'public', 'upload');
    // 保留文件后缀名
    form.keepExtensions = true;
    form.parse(req, (err, fiels, files) => {
        var result = {};
        for (let k in files) {
            // 处理前端上传的文件的保存路径
            result[k + 'path'] = files[k].path.split('public')[1];
        }
        // 将路径信息响应给浏览器用于实现预览功能
        res.send(result);
    })
})

最后实现的结果如下:

在这里插入图片描述
可能你在自己实现进度条功能时由于文件太小或者网速太快导致进度条一下子就满了。为了更加确定进度条功能没有问题,可以将浏览器的网络状态设置为 slow 3G ,你会发现此时上传速度相当慢。

在这里插入图片描述

设置网络后上传文件(注意此时不能关闭开发者工具,不然浏览器的网络还是 No throttling )

在这里插入图片描述

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值