js小白浅谈XMLHttpRequest文件上传

1,820 阅读1分钟

前端

文件选择=>文件信息获取=>数据打包=>向服务器端发送数据=>接收服务器端反馈的信息

服务端

创建服务=>端口监听等待数据=>接收数据=>数据整理

页面代码

因为注重功能所以页面代码不够绚丽,大佬们可以使用element-ui框架进行页面渲染

    <input type="file" id="file" /> <button>上传</button></br>
    <div class="picture"></div>

文件处理

   document
        .getElementById('file').addEventListener("change", event => {
            let file = event.target.files[0];
            FlieChunkList.push(file);
            pic.innerHTML += `
           <img src="${window.URL.createObjectURL(file)}" id='pic' width="500" />`
            });

当我们选择文件的时候将文件的信息拉取(这里我选择图片)下来 window.URL.createObjectURL(file)获取图片在本地的路径,图片预览。

            .querySelector('button').addEventListener('click', event => {
                requestListout = FlieChunkList.map((file) => { //将数据打包
                    const formData = new FormData(); //http传输时from表单数据类型
                    formData.append("chunk", file);//向from表单中添加数据
                    return {
                        formData
                    }
                }).map(({
                    formData
                }) => request({
                    url: 'http://localhost:8088/', //设置访问服务端口
                    data: formData//添加上传的数据
                }));
            })
 function request({//将请求打包
            url,
            method = 'POST',
            data,
            headers = {},
        }) {
            return new Promise(resolve => { //  在Premise中 花时间的东西(请求)异步操作成功  则运行这一步
                const xhr = new XMLHttpRequest(); //新建一个XMLHttpRequest的实例
                xhr.open(method, url); //请求  向远程主机发出一个HTTP请求。
                Object.keys(headers)//拿到headers中的所key值
                .forEach(key =>将key添加到xhr请求头中
                    xhr.setRequestHeader(key, headers[key]) //加请求头
                );
                xhr.send(data); 
                xhr.onload = event => { //监听传输完成时候 
                    alert(event.target.response);//显示上传信息
                };
            });
        }

第一次使用map方法将我们选择的数据放在fromData中以字面量组的形式返回

第二次使用map是将每一个数据加上request请求,因为xhr对象发送请求是一个异步过程,所以我们只要监听服务端返回的信息。

服务端的搭建

(1)准备需要

我们需要创建http服务需要引入http的模块const http = require('http');, 引入文件操作模块const path = require('path'); 加入我们解析客户端发来的FromData数据的模块 首先我们要将模块加载到本地 npm install fse-extra -D 按需引入,可以减少加载时间const multiparty = require('./node_modules/multiparty');加载文件操作的模块fs的扩展包fs-extranpm install --save-dev fs-extra引入const fse = require('./node_modules/fs-extra');.

(2)服务创建

const server = http.createServer();
server.on("request", async(req, res) => {
}
server.listen(3030, () =>//服务端口监听
    console.log("正在监听3000端口")
);

目录的创建时一个异步问题,所以我们使用异步串行的方法async-await

对于该知识的了解阮一峰老师的这篇文章讲的不错www.ruanyifeng.com/blog/2015/0…

(3)数据拉取

if (req.url == '/') {
        const multipart = new multiparty.Form(); //将form表单里面的数据取出来 
        multipart.parse(req, async(err, fields, files) => {
            if (err) {
                res.end("上传失败!");
                return;
            }
            const [chunk] = files.chunk;//解构multipart中的数据
            const filename = chunk.originalFilename;//获取文件名字
            const dir_name = filename.split('.')[0];
            const chunkDir = path.resolve(UPLOAD_DIR, dir_name);
            if (!fse.exists(chunkDir)) {
                await fse.mkdirs(chunkDir);//文件目录创建
            }
            await fse.move(chunk.path, `${chunkDir}/${chunk.originalFilename}`, (err) => {
                if (err) {
                    res.end("文件存在!")
                    return;
                }
                res.end("上传成功!")
            });
        });
    }

代码思路:拉取客户端数据=>存放目录添加=>目标文件移入=>信息反馈

await fse.mkdirs(chunkDir); 目录创建是一个异步过程,需要路径合成完成才能创建,当合成完毕执行目录创建。

问题来了!那我们为什么说目标文件移入呢?

因为我们在用multiparty获取FromData数据表单的时候已经把数据缓存在我们服务的缓存文件中了 所以我们需要将文件移到我们的指定的目录。同样http数据传输是一个Stream的过程是异步的,当我们等数据传输完毕,才能够移目标文件。

跨域资源共享的问题

因为我们是访问跨域资源,浏览器不知道怎么与服务器沟通的功能,浏览器会驳回请求,则需要设置头部 res.setHeader("Access-Control-Allow-Origin", "*"); 跨域资源问题底层知识,想了解的可以查阅CORS相关知识。

总结

在文件创建的时候,要注意异步问题然而async和await的搭配使用解决了这个问题