前后端交互——模拟QQ相册

<!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>Document</title>
    <link rel="stylesheet" href="css/photo.css">
</head>

<body>
    <!-- 展示相关 -->
    <div class="container">
        <div class="photoHeader">
            <div class="imgContainer">
                <img class="photoName" src="img/1.jpg" />
            </div>
            <div class="btnContainer">
                <span class="photoTitle">相册名称</span>
                <button class="mybtn">上传照片</button>
            </div>
        </div>

        <div class="photoContainer">
            <div class="photoItem">
                <img src="img/1.jpg" />
                <span>
                    home
                </span>
            </div>
        </div>
    </div>
    <!-- 上传相关 -->
    <div class="masking">
        <div class="addPhotoContainer"></div>
        <div class="addController">
            <h3 class="addTitle">上传照片-普通上传(H5)<span class="close"></span></h3>
            <div class="photoTitles">
                <span class="uploadTo">上传到</span>
                <div class="photoSelect">
                    <img class="showPhoto" src="img/1.jpg" />
                    相册名称
                </div>
            </div>

            <!-- 上传按钮 -->
            <div class="showContainer">
                <div class="uploadContainer">
                    <span class="fileinput-button">
                        <span>上传图片</span>
                        <input class="imgFile" type="file" name="" multiple="multiple" />
                    </span>
                    <span class="hint">
                        按住Ctrl可多选
                    </span>
                </div>
            </div>

            <!-- 显示待上传图片  -->
            <div class="loadContainer">
                <div class="wantUpload">
                    <div class="uploadPhotoItem">
                        <!-- <span class="myProgress">
                            <span class="plan"></span>
                            30%
                        </span>
                        <img src="img/1.jpg" />
                        <span class="pictureName">
                            home
                        </span> -->
                    </div>
                </div>
                <div class="addStyle">
                    <span class="fileinput-add">
                        <span></span>
                        <input class="imgFile-add" type="file" multiple="multiple" />
                    </span>
                </div>
                <!-- 开始上传按钮 -->
                <div class="bottomStyle">
                    <span class="uploadBtn">开始上传</span>
                </div>
            </div>

        </div>
    </div>
</body>
<script>
    document.querySelector(".close").onclick = function () {
        document.querySelector(".masking").style.display = "none";
    }
    document.querySelector(".mybtn").onclick = function () {
        document.querySelector(".masking").style.display = "block";
    }

    let uploadArr = [];
    /*
        用户添加图片会有添加两张后,想继续添加图片,不管添加几次,都是一个陆续到达的状态,
        所以需要一个容器里去承载 这个数组就是添加图片的容器,完全接受到之后,再去对这个容器 也就是数组,做相关的处理
        通过这个数组可以处理到所有上传的图片,也就是说这个数组是在中间先去过滤处理一下
    */

    document.querySelector(".imgFile").onchange = function () {
        document.querySelector(".loadContainer").style.display = "block";
        document.querySelector(".showContainer").style.display = "none";


        // console.log(this.files); // 获取到input框里的文件

        /*
            this.files是一个类数组   ...剩余参数 就可以用foreach了
            根据用户上传了几张图片,来决定你要在显示区创建几个DIV
            注意:在使用剩余参数时,上一行的代码结束位置必须要加分号!!!不然会报错
        */
        [...this.files].forEach(file => {

            // 每次for循环,从input里拿到的 数据 都给 这个类 让这个类去帮我处理并返还给我一个新的数据
            let uploadObj = new UploadImg(file);

            // 没返还一个新的数据,就给他添加到这个数组里
            uploadArr.push(uploadObj);

            /*
                uploadObj 是 UploadImg() 实例化出来的;
                createHtml() 是 UploadImg() 里的方法
                也就是说:foreach每次去input标签里去拿一个文件,拿一个给这个类处理一下,处理完,添加到数组里,
                         然后调用createHtml() 渲染到页面;
                         拿一个渲染一个,拿是个渲染十个————根据数组的数量来
            */
            uploadObj.createHtml();
        });
    };
    document.querySelector(".imgFile-add").onchange = function () {
        document.querySelector(".loadContainer").style.display = "block";
        document.querySelector(".showContainer").style.display = "none";

        /*
            因为files是类数组,直接调用foreach会报错,所以加上... 剩余参数
            根据用户上传了几张图片,来决定你要在显示区创建几个div
            注意:在使用剩余参数时,上一行的代码结束位置必须加分号!!!不然会报错
        */ 
        [...this.files].forEach(file => {
            //每遍历一次,从input里拿到数据 都给这个类,让这个类去处理并返回一个新的数据
            let uploadObj = new UploadImg(file);

            // 每返还一个新的数据,就给他添加到数据里
            uploadArr.push(uploadObj);

            // 每次去input里拿一次数据,就给类去处理一下,然后添加到数组里,然后调用这个方法,渲染到页面
            uploadObj.createHtml();
        });
    };

    class UploadImg {
        /*
            constructor在类里的作用:在原型中,有一个constructor
                                    它是一个指针,他会指回构造函数
                                    在类里整个都是原型的空间,在里边写的所有东西都是原型用的
                                    要想写点属性写在构造函数里,需要写在constructor里

            为什么要使用 类 : 因为这里的功能很多,我们需要给他封装一下

            为什么不使用函数封装 :因为他有很多个功能,我们需要把它单独写开。这样就没办法写在函数里,而类会更合适

        */
        constructor(file) {
            this.file = file;
            this.ele = null;
        }
        createHtml() {
            // 把文件转换为base64的格式 只是转了格式,内容不变
            let fileReader = new FileReader();
            fileReader.readAsDataURL(this.file);

            /*
                转换完成后执行的事件
                this指向问题:函数内直接写this指向的是 fileReader 不是我们外界传进来的this了
                两种解决方式:
                            1. 写成箭头函数
                            2. 存一下this
            */
            let _this = this;
            fileReader.onload = function () {
                // console.log(fileReader.result);  // 会把图片转换完成的信息返回,相当于图片的地址

                // 创建一个div
                let uploadDiv = document.createElement("div");

                // 添加样式
                uploadDiv.classList.add("uploadPhotoItem");

                // 给这个div拼接内容
                uploadDiv.innerHTML = `
                                            
                                            0%
                                        
                                        ${fileReader.result}" />
                                        
                                            ${_this.file.name}
                                        `;

                // 渲染到页面
                document.querySelector(".wantUpload").appendChild(uploadDiv);
                _this.ele = uploadDiv;
            }
        }

        /*
            依次上传:
                    一个一个的上传,需要用到异步上传
            一步有三个处理方式 : 1. 回调函数(回调地狱)
                                2. Promise对象
                                3. es7处理方式 async await
        */
        uploadFile() {

            return new Promise((resolve, reject) => {
                // 使用ajax上传
                let xhr = new XMLHttpRequest();
                xhr.open("post", "/upload", true);  //这里不能是get
                let form = new FormData();  // 放在form中
                form.append("img", this.file);// 设置一下key键 和 上传的内容  这里不需要转base64 不然在服务器页面还需转回来,比较麻烦无意义
                xhr.onload = function () {
                    console.log(xhr.responseText);  // 打印返回值
                };
                // 写进度条
                xhr.upload.onprogress = (evt) => {
                    /*
                        当前文件上传的大小 : evt.loaded
                        需要传输的总大小 : evt.total
                        为什么*100 : 因为他要100%,不然是零点零零几% 大的时候1%
                                    当 evt.loaded 和 evt.total 相等的时候 :也就是传完了 相除等于1 不符合我们的日常习惯
                    */
                    let percent = Math.round(evt.loaded / evt.total * 100);
                    /*
                        把完成的进度,赋值给标签

                        添加  这个类的时候直接找的话,后续可能还会有很多,未来会有很多
                        且this找不到!!!
                        解决方式:1. 149行 要写 this.ele = null; 
                                    185行写上_this.ele = uploadDiv; 每添加一个,就把他存在_this.ele 里
                                2. 这里写成箭头函数

                        为什么这里是 % 不是px或者rem:
                                                上传50% 就让他的宽度为 50%
                                                上传100% 就让他的宽度为 100%
                    */
                    this.ele.querySelector(".plan").style.width = percent + "%";    //进度条
                    this.ele.querySelector(".percentText").innerHTML = percent + "%";   //文字
                    this.ele.querySelector(".myProgress").style.display = "block";  //确保整个span可以出现,做一个保险措施

                    // 当前文件上传的大小 >= 需要传输的总大小
                    if (evt.loaded >= evt.total) {
                        console.log("上传完成");
                        resolve();

                    }
                };
                // 发送给后端接口
                xhr.send(form);
            });

        }
    }

    document.querySelector(".uploadBtn").onclick = function () {
        /*
            imgfile --> 数组里边遍历出来的每一个对象

            uploadObj 是 UploadImg() 实例化出来的;然后被添加到了uploadArr
            也就是说,imgfile是UploadImg()的实例化对象
            然后用imgfile调用类里的uploadFile()这个方法
        */
        // uploadArr.forEach(imgfile => {
        //     imgfile.uploadFile();
        // });

        // 依次上传
        async function que() {
            for (let i = 0; i < uploadArr.length; i++) {
                await uploadArr[i].uploadFile();    // 数组的每一个元素,调用uploadFile()这个方法
            };
            // 上传完成后
            document.querySelector(".wantUpload").innerHTML = "";   // 清空存放div的容器
            document.querySelector(".masking").style.display = "none";  // 上传完成后,回到准备页面
            document.querySelector(".loadContainer").style.display = "none";    //关闭
            document.querySelector(".showContainer").style.display = "block";   // 开启
        }
        que();
	}
</script>

</html>
//============================================
//服务器文件
const Koa = require("koa");
const Router = require("koa-router");
const static = require("koa-static");

let app = new Koa();
let router = new Router();

app.use(static(__dirname+"/static"));

router.get("/",ctx=>{
    ctx.body = "你好"
});
router.post("/upload",ctx=>{
    ctx.body = "some value..."
})

app.use(router.routes());
app.listen(8080);

你可能感兴趣的:(前端)