zoukankan      html  css  js  c++  java
  • 百度ueditor富文本编辑器实现上传文件、图片、视频到ftp服务器并在页面端回显

    之前一直用ueditor作为富文本编辑器,想要实现图片上传到ftp的功能,网上搜索了一圈,发现都感觉讲的不是很明白,而且有不少要引入其他的java代码,最近自己试了一下,终于调通了,希望能帮到各位。废话不多说,查看步骤。(注意,这个是springboot版本,其实springmvc的也差不多,有一点点不同,最后我会说明)

      1.将utf8-jsp的整个文件夹复制到resources的static目录下,打开之后目录如下。


    项目目录如下(我多加了一层ueditor目录,不过也一样)

     

    2.写两个controller,代码如下(请忽略楼主潦草的代码,自己拿回去调整一下,修改一下UploadController中ftp的地址,密码即可,总共有三个地方)

    1.UploadController

      

    package com.wang.controller;
    
    import lombok.extern.slf4j.Slf4j;
    import org.apache.commons.fileupload.FileItem;
    import org.apache.commons.fileupload.disk.DiskFileItemFactory;
    import org.apache.commons.fileupload.servlet.ServletFileUpload;
    import org.apache.commons.net.ftp.FTP;
    import org.apache.commons.net.ftp.FTPClient;
    import org.apache.commons.net.ftp.FTPConnectionClosedException;
    import org.apache.commons.net.ftp.FTPReply;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.bind.annotation.ResponseBody;
    
    import javax.imageio.ImageIO;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.awt.image.BufferedImage;
    import java.io.*;
    import java.net.URLEncoder;
    import java.text.SimpleDateFormat;
    import java.util.*;
    
    @RequestMapping("/ueditor/file")
    @Controller
    @Slf4j
    public class UploadController {
    
        private FTPClient ftpClient = new FTPClient();
        private Logger logger = LoggerFactory.getLogger(UploadController.class);
    
        //上传图片并回显
        @RequestMapping("/uploadImg")
        @ResponseBody
        public Map<String,Object> upload(HttpServletRequest request) throws Exception {
            Map<String ,Object> map = new HashMap<>();
            // 判断enctype属性是否为multipart/form-data
            boolean isMultipart = ServletFileUpload.isMultipartContent(request);
            if (!isMultipart)
                throw new IllegalArgumentException(
                        "上传内容不是有效的multipart/form-data类型.");
    
            // Create a factory for disk-based file items
            DiskFileItemFactory factory = new DiskFileItemFactory();
    
            // Create a new file upload handler
            ServletFileUpload upload = new ServletFileUpload(factory);
    
            // Parse the request
            List<FileItem> items = upload.parseRequest(request);
    
            Iterator iter = items.iterator();
            while (iter.hasNext()) {
                FileItem item = (FileItem) iter.next();
                    // 如果是文件字段
                    String fieldName = item.getFieldName();
                    String orginFileName = item.getName();
                    String imgSunffix = orginFileName.substring(orginFileName.lastIndexOf("."));
                    String uuid = UUID.randomUUID().toString().replaceAll("-", "");
                    String fileName = uuid+imgSunffix;
    //                String contentType = item.getContentType();
    //                boolean isInMemory = item.isInMemory();
    //                long sizeInBytes = item.getSize();
    
    //                String filePath = STORE_FILE_DIR + fileName;
                    //写入文件到当前服务器磁盘
    //                File uploadedFile = new File(filePath);
    
                    SimpleDateFormat formatPath = new SimpleDateFormat("yyMMdd");
                    SimpleDateFormat fromahhPath = new SimpleDateFormat("HH");
                    String yyMMdd =  formatPath.format(new Date());
                    String hours = fromahhPath.format(new Date());
                    String ftpfilePath = yyMMdd + "/" + hours;
    
    //                FileInputStream fileInputStream = (FileInputStream) item.getInputStream();
                    InputStream inputStream = item.getInputStream();
    
                    BufferedInputStream bufferedInputStream = null;
                    bufferedInputStream = new BufferedInputStream(inputStream);
                    FTPClient ftp = new FTPClient();
                    try {
                        int reply;
                        ftp.connect("11111", 21);// 连接FTP服务器
                        // 如果采用默认端口,可以使用ftp.connect(url)的方式直接连接FTP服务器
                        ftp.login("11111", "11111");// 登录
                        ftp.setFileType(FTP.BINARY_FILE_TYPE);
                        ftp.setFileTransferMode(FTP.STREAM_TRANSFER_MODE);
                        reply = ftp.getReplyCode();
                        if(!FTPReply.isPositiveCompletion(reply)){
                            ftp.disconnect();
                            throw new IOException("ftp登录失败");
                        }
                        for (String str:ftpfilePath.split("/")){
                            ftp.makeDirectory(str);
                            ftp.changeWorkingDirectory(str);
                        }
                        boolean tag = ftp.appendFile(fileName,bufferedInputStream);
                        if(!tag){
                            throw new Exception("保存到FTP失败");
                        }
                    }catch (Exception e){
                        e.printStackTrace();
                    }finally {
                        try {
                            inputStream.close();
                            ftp.logout();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                        if(ftp.isConnected()){
                            try {
                                ftp.disconnect();
                            }catch (IOException e){
                                e.printStackTrace();
                            }
                        }
                    }
                    String path = "/"+ftpfilePath+"/"+fileName;
    
    //                item.write(uploadedFile);
                    map.put("state","SUCCESS");
                    map.put("url",path);
                    map.put("title",orginFileName);
                    map.put("original",orginFileName);
            }
    //        response.setCharacterEncoding("UTF-8");
    //        response.getWriter().println("上传成功");
            return map;
        }
    
        //回显图片
    
        @RequestMapping("/getImg.do")
        @ResponseBody
        public void getImg( HttpServletResponse response,@RequestParam("path")String imgPath) {
            FTPClient ftp = null;
            InputStream in = null;
    //        imgPath = "ftpfile\upload\11.png";
            OutputStream os = null;
            ftp = new FTPClient();
            ftp.setConnectTimeout(1000 * 30);//设置连接超时时间
            ftp.setControlEncoding("utf-8");//设置ftp字符集
            ftp.enterLocalPassiveMode();//设置被动模式,文件传输端口设置
            try {
                int reply;
                ftp.connect("11111", 21);// 连接FTP服务器
                // 如果采用默认端口,可以使用ftp.connect(url)的方式直接连接FTP服务器
                ftp.login("11111", "11111");// 登录
                ftp.setFileType(FTP.BINARY_FILE_TYPE);
                ftp.setFileTransferMode(FTP.STREAM_TRANSFER_MODE);
                reply = ftp.getReplyCode();
                if (!FTPReply.isPositiveCompletion(reply)) {
                    ftp.disconnect();
                    throw new IOException("FTP登录失败");
                }
                //下载文件 FTP协议里面,规定文件名编码为iso-8859-1,所以读取时要将文件名转码为iso-8859-1
                //如果没有设置按照UTF-8读,获取的流是空值null
                in = ftp.retrieveFileStream(new String(imgPath.getBytes("UTF-8"), "iso-8859-1"));
                String picType = imgPath.split("\.")[1];
    
                BufferedImage bufImg = null;
                bufImg = ImageIO.read(in);
                os = response.getOutputStream();
                ImageIO.write(bufImg, picType, os);
    
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    in.close();// 在这里关闭,保证一定要关闭
                    ftp.logout();// 在这里登出,保证一定要登出
                } catch (IOException e) {
                    e.printStackTrace();
                }
                if (ftp.isConnected()) {
                    try {
                        ftp.disconnect();
                    } catch (IOException ioe) {
                    }
                }
            }
            response.setContentType("image/jpg");
        }
    
    
        /**
         * 下载文件到浏览器
         * @param response
         * @throws IOException
         */
        @RequestMapping("/downloadFile.do")
        @ResponseBody
        public  void downFile(@RequestParam("path")String path, HttpServletResponse response) throws IOException {
    // 获取ftp信息
    
            // 获取文件名称
            String[] strs = path.split("/");
            String downloadFile = strs[strs.length - 1];
            try {
                // 设置文件ContentType类型,这样设置,会自动判断下载文件类型
                response.setContentType("application/x-msdownload");
                // 设置文件头:最后一个参数是设置下载的文件名并编码为UTF-8
                response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(downloadFile, "UTF-8"));
                // 建立连接
                connectToServer();
    
                ftpClient.enterLocalPassiveMode();
                // 设置传输二进制文件
                int reply = ftpClient.getReplyCode();
    //            ftpClient.changeWorkingDirectory("./home/ftp");
                if(!FTPReply.isPositiveCompletion(reply)) {
                    ftpClient.disconnect();
                    throw new IOException("failed to connect to the FTP Server:"+21);
                }
                // 此句代码适用Linux的FTP服务器
                String newPath = new String(path.getBytes("GBK"),"ISO-8859-1");
                // ftp文件获取文件
                InputStream is = null;
                BufferedInputStream bis = null;
                try {
                    is = ftpClient.retrieveFileStream(newPath);
                    bis = new BufferedInputStream(is);
                    OutputStream out = response.getOutputStream();
                    byte[] buf = new byte[1024];
                    int len = 0;
                    while ((len = bis.read(buf)) > 0) {
                        out.write(buf, 0, len);
                    }
                    out.flush();
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    closeConnect();
                    if (bis != null) {
                        try {
                            bis.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                    if (is != null) {
                        try {
                            is.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }
            } catch (FTPConnectionClosedException e) {
                logger.error("ftp连接被关闭!", e);
                throw e;
            } catch (Exception e) {
                logger.error("ERR : upload file "+ downloadFile+ " from ftp : failed!", e);
            }
    
        }
    
        /**
         * 连接到ftp服务器
         */
        private void connectToServer() throws Exception {
    
    //        ftpClient.connect(server,21);
            if (!ftpClient.isConnected()) {
                int reply;
                try {
                    ftpClient=new FTPClient();
                    ftpClient.connect("111111", 21);// 连接FTP服务器
                    // 如果采用默认端口,可以使用ftp.connect(url)的方式直接连接FTP服务器
                    ftpClient.login("111111", "111111");// 登录
                    reply = ftpClient.getReplyCode();
    
                    if (!FTPReply.isPositiveCompletion(reply)) {
                        ftpClient.disconnect();
                        logger.info("connectToServer FTP server refused connection.");
                    }
    
                }catch(FTPConnectionClosedException ex){
                    logger.error("服务器:IP:"+21+"没有连接数!there are too many connected users,please try later", ex);
                    throw ex;
                }catch (Exception e) {
                    logger.error("登录ftp服务器【"+21+"】失败", e);
                    throw e;
                }
            }
        }
    
        /**
         *
         * 功能:关闭连接
         */
        public void closeConnect() {
            try {
                if (ftpClient != null) {
                    ftpClient.logout();
                    ftpClient.disconnect();
                }
            } catch (Exception e) {
                logger.error("ftp连接关闭失败!", e);
            }
        }
    
    
    }
    

      2.ServerController

      

    package com.wang.controller;
    import com.alibaba.fastjson.JSON;
    import com.alibaba.fastjson.JSONObject;
    import com.baidu.ueditor.ActionEnter;
    import org.springframework.core.io.ClassPathResource;
    import org.springframework.stereotype.Controller;
    import org.springframework.util.ClassUtils;
    import org.springframework.web.bind.annotation.RequestMapping;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.FileInputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.PrintWriter;
    
    //ueditor单独上传的controller
    @Controller
    public class ServerController {
        @RequestMapping("/configNew")
        public void config(HttpServletRequest request, HttpServletResponse response){
            response.setContentType("application/json");
    //        String res = ClassUtils.getDefaultClassLoader().getResource("km").getPath();
    //        System.out.println("res:"+res);
            System.out.println("路径:"+ClassUtils.getDefaultClassLoader().getResource("").getPath());
    //        String rootPath = ClassUtils.getDefaultClassLoader().getResource("../../").getPath() + "static/ueditor/jsp";
            String rootPath = ClassUtils.getDefaultClassLoader().getResource("").getPath() + "static/ueditor/jsp";
            System.out.println("root路径:"+rootPath);
            try {
                response.setCharacterEncoding("UTF-8");
                String exec = new ActionEnter(request, rootPath).exec();
                System.out.println(exec);
                PrintWriter writer = response.getWriter();
                writer.write(new ActionEnter(request, rootPath).exec());
                writer.flush();
                writer.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
    
    //    public void getUEConfig(HttpServletRequest request, HttpServletResponse response){
    //        org.springframework.core.io.Resource res = new ClassPathResource("config.json");
    //        InputStream is = null;
    //        response.setHeader("Content-Type" , "text/html");
    //        try {
    //            is = new FileInputStream(res.getFile());
    //            StringBuffer sb = new StringBuffer();
    //            byte[] b = new byte[1024];
    //            int length=0;
    //            while(-1!=(length=is.read(b))){
    //                sb.append(new String(b,0,length,"utf-8"));
    //            }
    //            String result = sb.toString().replaceAll("/\*(.|[\r\n])*?\*/","");
    //            JSONObject json = JSON.parseObject(result);
    //            PrintWriter out = response.getWriter();
    //            out.print(json.toString());
    //        } catch (IOException e) {
    //            e.printStackTrace();
    //        }finally {
    //            try {
    //                is.close();
    //            } catch (IOException e) {
    //                e.printStackTrace();
    //            }
    //        }
    //    }
    
    }
    

     3.修改ueditor.config.js

       

       

    var URL = window.UEDITOR_HOME_URL || getUEBasePath();
        var server_url = window.location.protocol+"//"+window.location.hostname+":"+window.location.port;
    
        /**
         * 配置项主体。注意,此处所有涉及到路径的配置别遗漏URL变量。
         */
        window.UEDITOR_CONFIG = {
    
            //为编辑器实例添加一个路径,这个不能被注释
            UEDITOR_HOME_URL: URL
    
            // 服务器统一请求接口路径
            // , serverUrl: URL + "jsp/controller.jsp"
            , serverUrl: server_url+"/configNew"

     4.修改ueditor的jsp目录下的config.json文件 目录图片如下

        打开这个文件之后修改大概7.8个地方的参数,都是xxxUrlPrefix,反正只要是图片注释就换成Ueditortxtcontroller中的getIMg路径,文件就换成downloadFile路径

      图片

       文件路径

      

      5.关键一步,记得在你的启动类application中排除掉有关文件上传的自动配置。

      

     如果是springmvc,那就是在xml文件中注释掉multipartfile这个bean了

     

     6.然后是改造的你的编辑器的访问页面index.html。

      

     在ue.ready方法中换掉相关原有的访问路径即可

     这是楼主的html页面,如果要用的话,记得把那个juqery-3.5.1.min.js和一个保存saveContent的方法排除掉就可以了

    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
            "http://www.w3.org/TR/html4/loose.dtd">
    <html>
    <head>
        <title>完整demo</title>
        <link REL="shortcut icon" href="/favicon.ico" type="image/ico"/>
        <meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
        <script type="text/javascript" charset="utf-8" src="/ueditor/ueditor.config.js"></script>
        <script type="text/javascript" charset="utf-8" src="/ueditor/ueditor.all.min.js"> </script>
        <script type="text/javascript" charset="utf-8" src="/js/jquery-3.5.1.min.js"> </script>
        <!--建议手动加在语言,避免在ie下有时因为加载语言失败导致编辑器加载失败-->
        <!--这里加载的语言文件会覆盖你在配置项目里添加的语言类型,比如你在配置项目里配置的是英文,这里加载的中文,那最后就是中文-->
        <script type="text/javascript" charset="utf-8" src="/ueditor/lang/zh-cn/zh-cn.js"></script>
    
        <style type="text/css">
            div{
                100%;
            }
        </style>
    </head>
    <body>
    <div>
        <h1>完整demo</h1>
        <script id="editor" type="text/plain" style="1024px;height:500px;"></script>
    </div>
    <div id="btns">
        <div>
            <button onclick="saveContent()">保存</button>
            <button onclick="getAllHtml()">获得整个html的内容</button>
            <button onclick="getContent()">获得内容</button>
            <button onclick="setContent()">写入内容</button>
            <button onclick="setContent(true)">追加内容</button>
            <button onclick="getContentTxt()">获得纯文本</button>
            <button onclick="getPlainTxt()">获得带格式的纯文本</button>
            <button onclick="hasContent()">判断是否有内容</button>
            <button onclick="setFocus()">使编辑器获得焦点</button>
            <button onmousedown="isFocus(event)">编辑器是否获得焦点</button>
            <button onmousedown="setblur(event)" >编辑器失去焦点</button>
    
        </div>
        <div>
            <button onclick="getText()">获得当前选中的文本</button>
            <button onclick="insertHtml()">插入给定的内容</button>
            <button id="enable" onclick="setEnabled()">可以编辑</button>
            <button onclick="setDisabled()">不可编辑</button>
            <button onclick=" UE.getEditor('editor').setHide()">隐藏编辑器</button>
            <button onclick=" UE.getEditor('editor').setShow()">显示编辑器</button>
            <button onclick=" UE.getEditor('editor').setHeight(300)">设置高度为300默认关闭了自动长高</button>
        </div>
    
        <div>
            <button onclick="getLocalData()" >获取草稿箱内容</button>
            <button onclick="clearLocalData()" >清空草稿箱</button>
        </div>
    
    </div>
    <div>
        <button onclick="createEditor()">
        创建编辑器</button>
        <button onclick="deleteEditor()">
        删除编辑器</button>
    </div>
    
    <script type="text/javascript">
    
        //实例化编辑器
        //建议使用工厂方法getEditor创建和引用编辑器实例,如果在某个闭包下引用该编辑器,直接调用UE.getEditor('editor')就能拿到相关的实例
        var ue = UE.getEditor('editor');
    
        ue.ready(function () {
            UE.Editor.prototype._bkGetActionUrl=UE.Editor.prototype.getActionUrl;
            UE.Editor.prototype.getActionUrl=function(action){
                if (action == 'uploadimage' ||action== 'uploadscrawl' || action == 'uploadimage') {
                    return "/ueditor/file/uploadImg";
                } else if(action =='uploadvideo') {
                    return "/ueditor/file/uploadImg";
                } else if(action == 'listimage'){
                    return "/ueditor/file/uploadImg";
                } else if(action == 'uploadfile'){
                    return "/ueditor/file/uploadImg";
                } else{
                    return this._bkGetActionUrl.call(this, action);
                }
            }
        });
        function isFocus(e){
            alert(UE.getEditor('editor').isFocus());
            UE.dom.domUtils.preventDefault(e)
        }
        function setblur(e){
            UE.getEditor('editor').blur();
            UE.dom.domUtils.preventDefault(e)
        }
        function insertHtml() {
            var value = prompt('插入html代码', '');
            UE.getEditor('editor').execCommand('insertHtml', value)
        }
        function createEditor() {
            enableBtn();
            UE.getEditor('editor');
        }
        function getAllHtml() {
            alert(UE.getEditor('editor').getAllHtml())
        }
        function saveContent() {
            var arr = [];
            arr.push(UE.getEditor('editor').getContent());
            $.ajax({
                type : 'post',
                dataType : 'json',
                url : '/ueditortxt/save',
                data:{
                    content: arr.join("
    ")
                },
                success:function(datas){
                    alert("添加成功");
                },
                error:function(a,b,c){
                    alert("添加失败");
                }
            });
        }
        function getContent() {
            var arr = [];
            arr.push("使用editor.getContent()方法可以获得编辑器的内容");
            arr.push("内容为:");
            arr.push(UE.getEditor('editor').getContent());
            alert(arr.join("
    "));
        }
        function getPlainTxt() {
            var arr = [];
            arr.push("使用editor.getPlainTxt()方法可以获得编辑器的带格式的纯文本内容");
            arr.push("内容为:");
            arr.push(UE.getEditor('editor').getPlainTxt());
            alert(arr.join('
    '))
        }
        function setContent(isAppendTo) {
            var arr = [];
            arr.push("使用editor.setContent('欢迎使用ueditor')方法可以设置编辑器的内容");
            UE.getEditor('editor').setContent('欢迎使用ueditor', isAppendTo);
            alert(arr.join("
    "));
        }
        function setDisabled() {
            UE.getEditor('editor').setDisabled('fullscreen');
            disableBtn("enable");
        }
    
        function setEnabled() {
            UE.getEditor('editor').setEnabled();
            enableBtn();
        }
    
        function getText() {
            //当你点击按钮时编辑区域已经失去了焦点,如果直接用getText将不会得到内容,所以要在选回来,然后取得内容
            var range = UE.getEditor('editor').selection.getRange();
            range.select();
            var txt = UE.getEditor('editor').selection.getText();
            alert(txt)
        }
    
        function getContentTxt() {
            var arr = [];
            arr.push("使用editor.getContentTxt()方法可以获得编辑器的纯文本内容");
            arr.push("编辑器的纯文本内容为:");
            arr.push(UE.getEditor('editor').getContentTxt());
            alert(arr.join("
    "));
        }
        function hasContent() {
            var arr = [];
            arr.push("使用editor.hasContents()方法判断编辑器里是否有内容");
            arr.push("判断结果为:");
            arr.push(UE.getEditor('editor').hasContents());
            alert(arr.join("
    "));
        }
        function setFocus() {
            UE.getEditor('editor').focus();
        }
        function deleteEditor() {
            disableBtn();
            UE.getEditor('editor').destroy();
        }
        function disableBtn(str) {
            var div = document.getElementById('btns');
            var btns = UE.dom.domUtils.getElementsByTagName(div, "button");
            for (var i = 0, btn; btn = btns[i++];) {
                if (btn.id == str) {
                    UE.dom.domUtils.removeAttributes(btn, ["disabled"]);
                } else {
                    btn.setAttribute("disabled", "true");
                }
            }
        }
        function enableBtn() {
            var div = document.getElementById('btns');
            var btns = UE.dom.domUtils.getElementsByTagName(div, "button");
            for (var i = 0, btn; btn = btns[i++];) {
                UE.dom.domUtils.removeAttributes(btn, ["disabled"]);
            }
        }
    
        function getLocalData () {
            alert(UE.getEditor('editor').execCommand( "getlocaldata" ));
        }
    
        function clearLocalData () {
            UE.getEditor('editor').execCommand( "clearlocaldata" );
            alert("已清空草稿箱")
        }
    </script>
    </body>
    </html>
    

      

    7.现在就可以访问这个页面然后上传文件、图片、视频并且回显了

      文件

     效果

     图片

     效果:

    视频:

     效果:

    这里有个问题,就是如果连续点击这个按钮,页面会回显不了视频,我发现可以把那个Ueditor的XSS过滤关掉就可以(注意,这个操作我也不知道会导致什么问题,慎重操作)

    关掉XSS过滤,修改ueditor.config.js中的三个参数为false即可

    至此就完成了,如果是springmvc的话怎么办呢?

    三步不同

    1.将serverController中上半部分代码注释掉,放开下半部分中注释掉的代码

    2.将改造过后的config.json文件放到resource下面

    3.pom文件中放开对json文件的限制,以便target中能够找到该文件

     项目地址:https://gitee.com/wl173/ueditorupload.git  看readme就行了

  • 相关阅读:
    img的srcset和sizes属性作用
    屏幕尺寸,分辨率,像素,PPI之间到底什么关系
    img 的 srcset、sizes 属性和 picture 元素
    收房细则
    购买雅居乐湖居笔记不得不知。
    Andoird Studio 错误: 非法字符: 'ufeff' 解决方案。
    解决mysql 1040错误Too many connections的方法
    Android布局优化之include、merge、ViewStub的使用
    美国人、英国人、中国人一生都是如何度过
    Android常用正则工具类
  • 原文地址:https://www.cnblogs.com/qiannianguyao/p/14790304.html
Copyright © 2011-2022 走看看