精华内容
下载资源
问答
  • 如何在Word中创建目录同时实现页码跳转,手把手教你,绝对让你懂!
  • flutter_file_selector 文件选择插件,支持多选 ,演示示例在example目录 我代码我传到码云上了,点我跳转 下面是效果图: 第三张是ios端 本插件使用到的依赖 插件依赖 pub仓库地址 ermission_handler ...

    flutter_file_selector 文件选择,支持多选,支持所有类型 ,演示示例在example目录
    插件我传到pub仓库上了,点我跳转
    码云上拉取项目,点我跳转
    Github上拉去项目,点我跳转

    下面是效果图: 第四张是ios端

    1.添加依赖

      # 在pubspec.yaml 中引入依赖 方法1 
      flutterfileselector: ^0.0.7
      或
      # 在pubspec.yaml 中引入依赖 方法2 
      flutterfileselector:
        git:
          url: https://gitee.com/jrnet/flutter_file_selector
    

    2.使用

    List<String> fileTypeEnd = [".pdf", ".doc", ".docx",".xls",".xlsx"];
    // 显示筛选按钮
    FlutterSelect(
        /// todo:  标题
        title: "选择文件",
        /// todo:  按钮
        btn: Text("选择文档"),
        /// todo:  最大可选
        maxCount: 3,
        /// todo:  往数组里添加需要的格式,默认是[".pdf", ".doc", ".docx",".xls",".xlsx"]
        fileTypeEnd: fileTypeEnd,
        /// todo:  自定义下拉选项,不传则默认
        dropdownMenuItem: [
            DropDownModel(lable: "文档",value: [".pdf",".txt",".word",".ppt"]),
            DropDownModel(lable: "图片",value: [".jpg",".png",".bmp",".jpeg",".gif"]),
        ],
        valueChanged: (v){
            
        },
    ),
    
    // 不显示筛选按钮
    FlutterSelect(
        /// todo:  标题
        title: "选择文件",
        /// todo:  按钮
        btn: Text("选择文档"),
        /// todo:  最大可选
        maxCount: 3,
        /// todo: 不展示筛选
        isScreen: false,
        /// todo:  往数组里添加需要的格式,默认是[".pdf", ".doc", ".docx",".xls",".xlsx"]
        fileTypeEnd: fileTypeEnd,
        valueChanged: (v){
       
        },
    ),
    

    注意:一定要有权限,安卓、ios都要配置

    安卓需配置目录访问权限 配置AndroidManifest.xml 文件,application里加入如下代码
    // tools:replace="android:resource"  需要导入tools 才能使用
    // AndroidManifest.xml 的 manifest 中 引入 xmlns:tools="http://schemas.android.com/tools"
    <provider
       android:name="androidx.core.content.FileProvider"
       android:authorities="${applicationId}.fileProvider"
       android:exported="false"
       android:grantUriPermissions="true"
       tools:replace="android:authorities">
       <meta-data
           android:name="android.support.FILE_PROVIDER_PATHS"
           android:resource="@xml/file_select_flutter"
           tools:replace="android:resource" />
    </provider>
    

    IOS 的配置 点此查看

    可选参数

    参数名
    String title 标题 - 默认:文件选择
    List fileTypeEnd 文件类型 - 默认:[".pdf", “.doc”, “.docx”,".xls",".xlsx"]
    bool isScreen 筛选 - 默认:关闭
    int maxCount 可选最大总数 - 默认 9
    List dropdownMenuItem 类型 - 默认:全部(fileTypeEnd)、文档、图片、视频、音频

    返回的参数:

    参数名
    File file 文件
    String fileName 文件名称
    int fileSize 文件大小
    String filePath 文件路径
    int fileDate 文件日期时间
    展开全文
  • 最近做了一个项目,是在网页中实现一份包含树目录导航的word文档的功能,简单点说就是将一个word文档,包括它的左侧导航部分,用网页的方式实现。 一、需求 1、 左侧实现目录树,右侧是内容 2、 点击左侧目录,...

    最近做了一个项目,是在网页中实现一份包含树目录导航的word文档的功能,简单点说就是将一个word文档,包括它的左侧导航部分,用网页的方式实现。

    一、需求

    1、  左侧实现目录树,右侧是内容

    2、  点击左侧目录,右侧跳转到对应的章节

    3、  左侧树要有全部展开、全部折叠、点击父节点前的加号可展开收缩、点击目录只可展开不可收缩、按关键字搜索目录等功能

    4、  右侧内容包含翻页,点击翻页时,右侧在跳转到对应页面的同时,左侧要选中右侧页面对应的章节目录

    二、思路

    1、用iframe分左右两栏,左侧是目录树页面,右侧是内容页面

    2、树用zTree实现

    三、实现过程

    1、iframe实现分栏

    <iframe height="100%" id="left" style="border:none"></iframe>
    <iframe height="100%" id="right" name="main"  style="border:none;"></iframe>

     

    $("iframe").css({
        "border" : "1",
        "border-color" : "#B2DFEE",
        "scrolling" : "yes"
    });
    $("#left").attr({
        "src" : "left.html",
        "width" : "20%"
    });
    $("#right").attr({
        "src" :"right.html"
        "width" : "79.7%"
    });

     上面的代码中有两个iframe,用jqueruy添加src属性,分别嵌入左侧的目录树left.html页面和右侧的right.html页面,并设置它们各自所占的宽度。

    2、实现左侧的目录树

    在实现树之前,要引入ztree的js库和css样式表以及jquery库,除此之外还可根据自己的需求,引入不同的js和css外部文件。如下所示:

    <link rel="stylesheet" type="text/css" href="../js/zTree/js/zTreeStyle/zTreeStyle.css" />
    <link rel="stylesheet" href="../js/bootstrap/css/bootstrap.min.css" />
    <script type="text/javascript" src="../js/jquery-1.9.1.min.js"></script>
    <script type="text/javascript" src="../js/bootstrap/js/bootstrap.min.js"></script>
    <script type="text/javascript" src="../js/zTree/js/jquery.ztree.core-3.5.min.js"></script>

    实现一棵ztree树,基本的操作有四步:

    第一步:设置树的id,注意用的是<ul></ul>标签

    <ul id="helpTree" class="ztree"></ul>

    第二步:配置树

    var setting = {
            edit :{
                enable:true
            },
            view : {
                dblClickExpand: false,
                showLine : true,
                checkable:true,
                showIcon:true,
                showTitle:true,
                fontCss:{
                    'color' : '#009ACD',//#009ACD
                    'font-weight' : 'normal'
                }
            },
            data : {
                simpleData : {
                    enable : true,
                    idKey : "id",
                    pIdKey : "pId",
                    rootPid :0
                },
                key:{
                    title:"title"
                }
            },
            check:{
                enable:false,//不显示checkbox、radio
                chkStyle:"checkbox",
                radioType:"level"
                
            },
            callback : {
                beforeClick:function(treeId,treeNode){
                    zTree=$.fn.zTree.getZTreeObj("helpTree");
                     if (treeNode.isParent) {
                            zTree.expandNode(treeNode,true);
                            return;
                        } 
                }, 
                beforeDblClick:function(treeId,treeNode){
                    return false;//禁止双击事件onDblClick
                },
                onExpand : function(event,treeId,treeNode) {
                    zTree = $.fn.zTree.getZTreeObj("helpTree");
                    zTree.expandNode(treeNode,true);
                },
                onCollapse : function(event,treeId,treeNode) {
                    zTree = $.fn.zTree.getZTreeObj("helpTree");
                    zTree.expandNode(treeNode,false);
                },
                onClick :onClick
                /* beforeCollapse:function(treeId,treeNode){
                    var id = treeNode.id;
                    if(id==1||id==2||id==3||id==4||id==5){
                        return true;//禁止所有
                    }else{
                        return false;
                    }
                }, */
                 
    
            }
        };
    
    
    /* 节点点击事件 :点击某个节点将其值赋值给文本框*/
        function onClick(treeId, treeNode) {
            //在不输入关键字选中节点时禁止点击事件的发生
            var search = $("#keyName").val();
            if (search == "" || search == null) {
                return;
            } else {
                var zTree = getTree();
                var nodes = zTree.getSelectedNodes();
                var v = "";
                for (var i = 0, l = nodes.length; i < l; i++) {
                    v += nodes[i].name + ",";
                }
                if (v.length > 1) {
                    v = v.substring(0, v.length - 1);
                }
                $("#keyName").attr("value", v);
                $("#keyName").attr("title", v);
                return false;
            }
    
        }

    第三步:设置树节点数据,以及各节点的链接页面

    var zNodes =[
                 /* 前言 */
                 { id:17, pId:0, name:"前言",url:"./foreword.html",target:"main",icon:"../js/zTree/js/zTreeStyle/img/diy/3.png"},
                   /* 第一章 */
                 { id:1, pId:0, name:"1 第一章", open:false,url:"./cluster/cluster_1.1.html",target:"main"},
                 { id:11, pId:1, name:"1.1 第一节", open:true,url:"./cluster/cluster_1.1.html",target:"main"},
                 { id:12, pId:1, name:"1.2 第二节群",open:true,url:"./cluster/cluster_1.2.1.html",target:"main"},
                 { id:120, pId:12, name:"1.2.1 第二节1群",open:true,url:"./cluster/cluster_1.2.1.html",target:"main"},
                 { id:121, pId:120, name:"1.2.1.1 第二节11", open:true,url:"./cluster/cluster_1.2.1.html",target:"main"},
                 { id:122, pId:120, name:"1.2.1.2第二节12", open:true,url:"./cluster/cluster_1.2.1.html#2",target:"main"},
                 { id:123, pId:120, name:"1.2.1.3 第二节13", open:true,url:"./cluster/cluster_1.2.1.html#3",target:"main"},
                 { id:124, pId:120, name:"1.2.1.4 第二节14", open:true,url:"./cluster/cluster_1.2.1.html#4",target:"main"},
                 { id:125, pId:120, name:"1.2.1.5 第二节15", open:true,url:"./cluster/cluster_1.2.1.html#5",target:"main"},
                 { id:126, pId:120, name:"1.2.1.6 第二节16", open:true,url:"./cluster/cluster_1.2.1.html#6",target:"main"},
                 { id:127, pId:12, name:"1.2.2 第二节2",open:true,url:"./cluster/cluster_1.2.2.html",target:"main"},
                 { id:13, pId:1, name:"1.3 第三节",open:true,url:"./cluster/cluster_1.3.html",target:"main"},
                 { id:131, pId:13, name:"1.3.1 第三节1",open:true,url:"./cluster/cluster_1.3.html",target:"main"},
                 { id:14, pId:1, name:"1.4 第四节",open:true,url:"./cluster/cluster_1.4.1.html",target:"main"},
                 { id:141, pId:14, name:"1.4.1 第四节1",open:true,url:"./cluster/cluster_1.4.1.html",target:"main"},
                 { id:142, pId:14, name:"1.4.2 第四节2",open:true,url:"./cluster/cluster_1.4.2.html",target:"main"},
                 { id:143, pId:14, name:"1.4.3 第四节3",open:true,url:"./cluster/cluster_1.4.3.html",target:"main"},
                 { id:15, pId:1, name:"1.5 第五节",open:true,url:"./cluster/cluster_1.5.html",target:"main"},
                 { id:16, pId:1, name:"1.6 第六节",open:true,url:"./cluster/cluster_1.6.html",target:"main"},
                 
                 /*第二章 */
                 { id:2, pId:0, name:"2 第二章",open:false,url:"./viewinfo/viewinfo_2.1.html",target:"main"},
                 { id:21, pId:2, name:"2.1第一节", open:true,url:"./viewinfo/viewinfo_2.1.html",target:"main"},
                 { id:22, pId:2, name:"2.2 第二节", open:true,url:"./viewinfo/viewinfo_2.2.html",target:"main"},
                 { id:23, pId:2, name:"2.3 第三节", open:true,url:"./viewinfo/viewinfo_2.3.html",target:"main"},
                 { id:24, pId:2, name:"2.4 第四节", open:true,url:"./viewinfo/viewinfo_2.4.1.html",target:"main"},
                 { id:241, pId:24, name:"2.4.1 第四节1", open:true,url:"./viewinfo/viewinfo_2.4.1.html",target:"main"},
                 { id:242, pId:24, name:"2.4.2 第四节2", open:true,url:"./viewinfo/viewinfo_2.4.2.html",target:"main"},
                 { id:25, pId:2, name:"2.5 第五节", open:true,url:"./viewinfo/viewinfo_2.5.1.html",target:"main"},
                 { id:251, pId:25, name:"2.5.1 第五节1", open:true,url:"./viewinfo/viewinfo_2.5.1.html",target:"main"},
                 { id:252, pId:25, name:"2.5.2 第五节2", open:true,url:"./viewinfo/viewinfo_2.5.2.html",target:"main"},
                 { id:253, pId:25, name:"2.5.3 3第五节", open:true,url:"./viewinfo/viewinfo_2.5.3.html",target:"main"},
                 { id:26, pId:2, name:"2.6 第六节", open:true,url:"./viewinfo/viewinfo_2.6.html",target:"main"},
                 
        

    第四步:初始化树,即形成树

    /* 初始化树 */
    function initialTree() {
        $.fn.zTree.init($("#helpTree"), setting, zNodes);
    }

    通过以上这些步骤,就实现了前两个需求:创建一棵目录树,并在点击左侧树的目录标题时右侧会跳转到相应的内容。

    关于树的配置,可以参考zTree的API进行查看,这里只是配置了一部分,根据不同的需求会有不同的配置,这里不加以阐述,因为我也还需要继续学习,毕竟我也是个后端儿!

    3、点击按钮实现全部展开和全部折叠功能

    第一步:创建按钮

    <a href="#" title="全部展开" class="btn btn-info btn-sm" οnclick="down()"> <span class="glyphicon glyphicon-plus" ></span></a> 
    <a href="#" title="全部折叠" class="btn btn-info btn-sm" οnclick="up()"> <span class="glyphicon glyphicon-minus" ></span></a>

    第二步:点击【全部展开】功能

    /*
    * 展开所有树节点
    */

    function
    down() { $.fn.zTree.getZTreeObj("helpTree").expandAll(true); }

    第三步:点击【全部折叠】功能

    /*
     * 折叠所有树节点
     */
    function up() {
        $.fn.zTree.getZTreeObj("helpTree").expandAll(false);
    }

    4、按关键字搜索树【该功能的实现是利用zTree中的getNodesByParamFuzzy("name", "test", null)方法进行模糊匹配】
    第一步:创建搜索输入框和按钮

    <input id="keyName" value="" type="text" placeholder="请输入搜索关键字"
    class="form-control" οnkeyup="searchClear()" style="width: 180px;">
    
    <a href="#" title="搜索" class="btn btn-info btn-sm" οnclick="search()"> <span class="glyphicon glyphicon-search" ></span></a>

    可以看到在input中有一个onkeyup()事件,这个事件会在键盘按键被松开时触发,即当用户准备输入时就会触发;另外在搜索按钮上也有一个点击事件,这个事件是当用户在输入搜索关键字,并点击搜索按钮时才会执行搜索动作
    第二步:用户输入时,根据输入信息实时匹配出搜索结果

    function searchClear() {
        var keyValue = $("#keyName").val();
       /* 当输入值为空时展示完整的树,并给树添加title属性*/
    if (keyValue == "") { clear(); } else { autoMatch(keyValue) } } /* 自动匹配搜索结果 */ function autoMatch(name) { var nameValue = name.replace(/\s+/g, ""); if (nameValue.length > 0) { initialTree(); var treeObj = getTree(); var nodeList = treeObj.getNodesByParamFuzzy("name", nameValue, null); /* 将搜索结果重新生成一棵树展示,并设置目录的title属性 */
    if (nodeList && nodeList.length > 1) {
    $.fn.zTree.init($(
    "#helpTree"), setting, nodeList); showTree(); setTitle(); } else { hideTree(); initialTree(); setTitle(); } } else { showTree(); } } //手动清除搜索内容时初始化并显示树 function clear() { initialTree(); showTree(); setTitle(); } /* 隐藏树 */ function hideTree() { $("#content").hide(); } /* 显示树 */ function showTree() { $("#content").show(); }
    /*给树节点设置title属性 */ function setTitle() { var zTree = getTree(); nodes = zTree.transformToArray(zTree.getNodes()); for (var i = 0; i < nodes.length; i++) { nodes[i].title = nodes[i].name; zTree.updateNode(nodes[i]); } }

    第三步:点击搜索按钮时进行搜索

    //点击搜索按钮进行模糊匹配
    function search() {
        var keyValue = $("#keyName").val();
        autoMatch(keyValue);
    }

    5、点击清空按钮时显示整个树

    当用户想改变搜索条件时,可以点击清空按钮重新输入搜索关键字,但是在清空搜索条件后,会还原树

    清空按钮:

    <a href="#" title="清空" class="btn btn-info btn-sm" onclick="clearKey()"> <span class="glyphicon glyphicon-remove" ></span></a>  

    清空功能:

    /* 点击清空按钮时清除搜索内容并显示初始化树 */
    function clearKey() {
        $("#keyName").val("");
        initialTree();
        showTree();
        setTitle();
        inPlaceholder();
    }
    
    /**
     * IE9和IE8中实现placeholder属性的功能
     * @param inputId
     */
    function inPlaceholder(){
        var inputId = "keyName";
        var isFocus = $("#"+inputId).is(":focus");
        var inputValue = $("#"+inputId).val();
        var phContent = $("#"+inputId).attr("placeholder");
        var isPlaceholder = "placeholder" in document.createElement("input");
        if(!isPlaceholder){
            //失去焦点并且input值为空时显示placeholder
            if(!isFocus){
                if(inputValue===""){
                    $("#"+inputId).val(phContent).css("color","gray");
                }
            }
            
            $("#"+inputId).focus(function(){
                var inputValue1 = $("#"+inputId).val();
                if(inputValue1 && inputValue1==phContent){
                    $("#"+inputId).val("");
                }else{
                    $("#"+inputId).val(inputValue1);
                }
            });
            
            $("#"+inputId).blur(function(){
                var inputValue1 = $("#"+inputId).val();
                if(inputValue1 == ""){
                    $("#"+inputId).val(phContent).css("color","gray");
                }else{
                    $("#"+inputId).val(inputValue1);
                }
            });
        }
    }

    上面的3、4、5步实现了第二个需求,并且在清空搜索条件中有一个inPlaceholder()方法,解决了IE8和IE9不支持H5新特性placeholder属性的问题

    6、实现翻页功能

    第一步:在每一个内容页创建【上一页】和【下一页】的链接或按钮

    <div  style="text-align: right;margin-top: 100px;margin-right: 100px;font: 25px;font-weight: bolder;">
    <a type="button" href="../cluster/cluster_1.6.html" style="text-decoration: none;"  class="lastbtn">上一节</a>
    <a type="button" href="../viewinfo/viewinfo_2.2.html" style="text-decoration: none;"  class="nextbtn">下一节</a>
    </div>

    第二步:给所有页面的上一页和下一页添加id属性

    $(".nextbtn").attr("id","nextBtn");
    $(".lastbtn").attr("id","lastBtn");

    第三步:点击翻页按钮,进行页面跳转

    $("#nextBtn").click(function(){
        selectNode(this);
    }) ;
    
    $("#lastBtn").click(function(){
        selectNode(this);
    }) ;
    
    /**
     * 调用左边iframe中的选中节点方法
     * @param id
     */
    function selectNode(id){
        var href = $(id).attr("href");
        var start = href.lastIndexOf("/");
        if(start!=-1){
            href=href.substr(start+1,href.length);
        }
        getParent().getNode(href);
    }
    
    function getParent(){
        var frames=window.parent.window.document.getElementById("left"); 
        return frames.contentWindow;
    }
    
    /* 读取并选中链接所对应的树节点 */
    function getNode(href) {
        var treeObj = getTree();
        var childNodes = treeObj.transformToArray(treeObj.getNodes());
        var url, id;
        for (var j = 0; j < childNodes.length; j++) {
            url = childNodes[j].url;
            id = childNodes[j].id;
            var start = url.lastIndexOf("/");
            var urlHref = url.substr(start+1, url.length);
            if (href == urlHref) {
                treeObj.selectNode(treeObj.getNodeByParam("id",id, null));
                return;
            }
        }
    }

    注意:上面的getNode(href)方法是在left.html页面,即在右侧页面的父页面中,所以在内容页面调用该方法时就要用到iframe在子页面获取父页面中元素或方法的方法getParent()

    下面是实现翻页并选中对应页面目录的思路:

    1、获取翻页链接中最后一个斜杠之后的内容right_href;

    2、遍历树节点中的链接,并获取最后一个斜杠之后的内容left_href;

    3、对比right_href和right_href,如果相同,则选中该节点;

    最后附上一张树的效果图

    以上就是这次遇到的问题及解决方法,里面延伸了一些内容,如IE8\9中不支持placeholder属性的解决方法,自己解决的有点击翻页选中跳转页面的目录,用户输入时进行匹配搜索结果等,

    除此之外,其他内容都是关于ztree的部分,zTree中的内容比较多,我这里只是显示,没有涉及到编辑树的内容,所以这部分还需继续学习研究,以后有时间再补充吧,此篇就到这里......

    转载于:https://www.cnblogs.com/hellowhy/p/6396901.html

    展开全文
  • 阅读目录 1. 后台处理的java 方法 2. Sping MVC框架实现下载遇到的“坑” ... 该博客记录Java Web项目将word打包zip并提供下载功能的实现和其中遇到的坑,方便后续自己的查看的参照。 项目架构背景: JQuery + B...

          该博客记录Java Web项目将word打包zip并提供下载功能的实现和其中遇到的坑,方便后续自己的查看的参照。
          项目架构背景: JQuery + Bootstarp + SpringMvc

    1. 后台处理的java 方法

          实现思路:首先将所有的word生成到uploadword目录下面,然后指定被压缩的文件夹为uploadword,并将生成的zip指定到uploadzip文件夹(在配置目录路径的时候记得注意几种不同的服务器路径写法),当时也考虑过在同一个文件夹下面生成word ,然后压缩为一个 zip,但很可惜压缩出来的文件,总是莫名奇妙的迭代了很多相同的压缩包,可能是将生成的压缩包,也作为了文件不断循环在压缩,所以果断分开文件夹。在将文件循环压缩入压缩包中后,删除原uploadword文件夹中的文件,所以当程序正确执行完后,服务器中的uploadword这个文件夹都是清空的(毕竟这个功能是权限比较大的管理员进行的操作,有且只有一个超级管理员,现在还没有考虑多用户同时并发生成压缩包的情况)

         根据 word 模版生成 word 文书的方法,我已经在前面的博客中总结过,可以看 java动态生成复杂word文件
    压缩方法上代码:

    复制代码
    1 /** 
     2 * @Description: 生成zip包
     3 * @param: web请求对象,web返回对象,web后端返回到前端的map
     4 * @return: TODO (返回类型和参数描述)
     5 */
     6 public void createZip(HttpServletRequest req,HttpServletResponse res,Map<String, Object> retmap) {
     7 try {
     8 //从配置文件获取生成压缩包的文件夹路径,生成唯一的压缩包名称
     9 String zippath = getUploadpath("uploadzip");
    10 String zipFileName = Functions.now("yyyy-MM-dd-HH-mm-ss") + ".zip";
    11 String zipFilePath = zippath + zipFileName;
    12 
    13 //生成压缩文件,并写入压缩流中
    14 File zipFile = new File(zipFilePath);
    15 ZipOutputStream zipOut = new ZipOutputStream(new FileOutputStream(zipFile));
    16 
    17 //从配置文件获取待压缩的的文件夹路径,并生成文件夹,准备好读入流,等待进行读入文件进行压缩
    18 String filepath = getUploadpath("uploadword");
    19 File file = new File(filepath);
    20 InputStream input = null;
    21 
    22 // 压缩包中文件zip的名称为
    23 zipOut.setComment(file.getName());
    24 
    25 //循环读入uploadword文件夹中文件,写入压缩流中,最后删除对应的文件
    26 if (file.isDirectory()) {
    27 File[] files = file.listFiles();
    28 for (int i = 0; i < files.length; ++i) {
    29 
    30 input = new FileInputStream(files[i]);
    31 zipOut.putNextEntry(new ZipEntry(file.getName()+ File.separator + files[i].getName()));
    32 
    33 int temp = 0;
    34 while ((temp = input.read()) != -1) {
    35 zipOut.write(temp);
    36 }
    37 input.close();
    38 files[i].delete();
    39 }
    40 }
    41 //关闭压缩流,并将压缩文件夹路径,压缩文件名称,当前服务器类型【tomcat,weblogic】返回给前端用于跳转下载页面时进行传参
    42 zipOut.close();
    43 retmap.put("zippath", zipFilePath);
    44 retmap.put("zipFileName", zipFileName);
    45 retmap.put("serverName", ServerDetector.getServerId().toUpperCase());
    46 retmap.put("success", true);
    47 } catch (Exception e) {
    48 e.printStackTrace();
    49 }
    50 }
    51 
    52 /** 
    53 * @Description: 获取配置文件上传路径(为了可维护性高,路径会配置在配置文件中,然后在代码中进行读,这样会减少后边维护人员的痛苦)
    54 */
    55 public String getUploadpath(String param) {
    56 Properties prop = new Properties();
    57 String url = this.getClass().getResource("").getPath().replaceAll("%20", " ");
    58 String path = url.substring(0, url.indexOf("WEB-INF")) + "WEB-INF/Config.properties";
    59 try {
    60 prop.load(new FileInputStream(path));
    61 } catch (Exception e) {
    62 e.printStackTrace();
    63 }
    64 String content = prop.getProperty(param).trim();
    65 return content;
    66 }
    复制代码

    2. Sping MVC框架实现下载遇到的“坑” 

        可能以前做过这功能朋友的会产生疑问,为什么要跳转到前端的下载页面去,不直接在后端生成下载流,放入HttpServletResponse对象,返回给前端直接弹出下载框。当时我也是这样想的,但事实总是很残酷。
    原来实现的代码是这样的:

    复制代码
    1 /** 
     2 * @Description: 实现压缩包下载
     3 * @param: TODO (入参描述)压缩文件夹的路径,生成的压缩文件的名称
     4 * @return: TODO (返回类型和参数描述)
     5 */
     6 public void downFile(HttpServletResponse response, String zippath, String zipFileName) {
     7 try {
     8 
     9 String path = zippath + zipFileName;
    10 File file = new File(path);
    11 if (file.exists()) {
    12 InputStream ins = new FileInputStream(path);
    13 BufferedInputStream bins = new BufferedInputStream(ins);// 放到缓冲流里面
    14 
    15 OutputStream outs = response.getOutputStream();// 获取文件输出IO流
    16 BufferedOutputStream bouts = new BufferedOutputStream(outs);
    17 
    18 response.reset();
    19 response.setContentType("application/x-download");// 设置response内容的类型
    20 response.setHeader("Content-disposition", "attachment;filename=" + URLEncoder.encode(str, "GBK"));// 设置头部信息
    21 int bytesRead = 0;
    22 byte[] buffer = new byte[8192];
    23 
    24 while ((bytesRead = bins.read(buffer, 0, 8192)) != -1) {
    25 bouts.write(buffer, 0, bytesRead);
    26 }
    27 bouts.flush();
    28 ins.close();
    29 bins.close();
    30 outs.close();
    31 bouts.close();
    32 } else {
    33 response.sendRedirect("/error.jsp");
    34 }
    35 } catch (Exception e) {
    36 e.printStackTrace();
    37 }
    38 }
    复制代码

        加入后台这段代码后,并没有达到我想要的效果,可能是我对Spring Mvc理解的不够,还请各位看官赐教。

    3. 生成zip包成功,在前端跳转下载页面

        既然后端不能直接返回下载流,并弹出下载框,在前端接受返回的下载所需(zippath,zipFileName,serverName)的关键参数,跳转到下载JSP页面。

    相应跳转(跳转方式多种多样,这里采用表单提交到download.jsp页面):

    复制代码
    var theParam = {};
    theParam.zippath = data.zippath;
    theParam.zipFileName =data.zipFileName;
    Param.serverName =data.serverName;
    
    var formStr = "<form action='..../download.jsp' method='post' id='form' style='display:none'>";
    $.each(theParam, function(key, value) {
    formStr += "<input type='hidden' name='" + key + "' value='" + value + "'/>";
    });
    formStr += "</form>";
    $("body").append(formStr);
    $("#form").submit();
    复制代码

    下载页面:

    复制代码
    <%@page import="java.io.OutputStream"%>
    <%@page import="java.net.URLEncoder"%>
    <%@page import="java.io.FileInputStream"%>
    <%@page language="java" contentType="application/x-msdownload" pageEncoding="UTF-8"%>
    <%
    try {
    //关于文件下载时采用文件流输出的方式处理:
    //加上response.reset(),并且所有的%>后面不要换行,包括最后一个;
    response.reset();//可以加也可以不加
    response.setContentType("application/x-download");
    String filedownload = request.getParameter("zippath");
    String filedisplay = request.getParameter("zipFileName");
    response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(filedisplay, "UTF-8"));
    
    OutputStream outp = response.getOutputStream();
    FileInputStream in = new FileInputStream(filedownload);
    byte[] b = new byte[1024];
    int i = 0;
    while ((i = in.read(b)) > 0) {
    outp.write(b, 0, i);
    }
    outp.flush();
    outp.close();
    in.close();
    //Tomcat 需要添加这两局来避免 getOutputStream() 方法,已被调用的异常,weblogic做特殊判断
    if(request.getParameter("serverName").equals("TOMCAT")){
    out.clear();
    out = pageContext.pushBody();
    }
    } catch (Exception e) {
    e.printStackTrace();
    }
    %>
    复制代码

    4. 流在不同web容器的妥善处理

        刚开始程序在tomcat 上跑时,总出现报错:
    org.apache.jasper.JasperException: java.lang.IllegalStateException: getOutputStream() has already been called for this response

        百度一番,发现Body()的作用是保存当前的out对象,并更新PageContext中Page范围内Out对象。JSP容器在处理完成请求后会调用releasePageConter方法释放所有的
    PageContestObject,并且同时调用getWriter方法。由于getWriter方法与在JSP页面中使用流相关的getOutputStream方法冲突,解决方法也很简单,重新生成PageContext中Page范围内Out对象。
    在代码最后添加 :
    out.clear();
    out = pageContext.pushBody();

        好了,自己本地测试不会报错了,将项目打包到weblogic,出现报错:
    Servlet failed with Exception java.lang.IllegalStateException: Response already committed at weblogic.servlet.internal.ServletResponseImpl.objectIfCommitted(ServletResponseImpl.java:1602) ......
        虽然不影响功能的使用,还是看着不爽,百度一番,发现是自己添加的上两句的的原因(tomcat 和 weblogic 容器的差异性,应该是weblogic并不会调用releasePageConter方法释放所有的PageContestObject)

        这就是下载页面出现这几行代码的原因:

    //Tomcat 需要添加这两局来避免 getOutputStream() 方法,已被调用的异常,weblogic做特殊判断
    if(request.getParameter("serverName").equals("TOMCAT")){
    out.clear();
    out = pageContext.pushBody();
    }

         如果能判断是在哪个web容器中,然后进行特殊判断就好了,同样是百度一番,发现 portal-kernel.jar中的类ServerDetector.java 能完美判断多达10种以上的容器类型,但我又不想将这个jar 引入到项目中(俺就用一个类,引一个jar,太亏了),然后Jd-Gui反编译,单独拉出这个类,修改修改添加到我的项目中。

    上代码(修改完的代码,不依赖任何类,可以拿来直接用):

    复制代码
     1 /**
      2 * @ClassName: ServerDetector
      3 * @Description: 判断 Web 容器类型
      4 */
      5 public class ServerDetector{
      6 private static ServerDetector _instance = new ServerDetector();
      7 private String _serverId;
      8 private Boolean _geronimo;
      9 private Boolean _glassfish;
     10 private Boolean _jBoss;
     11 private Boolean _jetty;
     12 private Boolean _jonas;
     13 private Boolean _oc4j;
     14 private Boolean _resin;
     15 private Boolean _tomcat;
     16 private Boolean _webLogic;
     17 private Boolean _webSphere;
     18 
     19 public static final String GERONIMO_ID = "geronimo";
     20 public static final String GLASSFISH_ID = "glassfish";
     21 public static final String JBOSS_ID = "jboss";
     22 public static final String JETTY_ID = "jetty";
     23 public static final String JONAS_ID = "jonas";
     24 public static final String OC4J_ID = "oc4j";
     25 public static final String RESIN_ID = "resin";
     26 public static final String TOMCAT_ID = "tomcat";
     27 public static final String WEBLOGIC_ID = "weblogic";
     28 public static final String WEBSPHERE_ID = "websphere";
     29 
     30 public static String getServerId() {
     31 ServerDetector sd = _instance;
     32 if (sd._serverId == null) {
     33 if (isGeronimo()) {
     34 sd._serverId = "geronimo";
     35 } else if (isGlassfish()) {
     36 sd._serverId = "glassfish";
     37 } else if (isJBoss()) {
     38 sd._serverId = "jboss";
     39 } else if (isJOnAS()) {
     40 sd._serverId = "jonas";
     41 } else if (isOC4J()) {
     42 sd._serverId = "oc4j";
     43 } else if (isResin()) {
     44 sd._serverId = "resin";
     45 } else if (isWebLogic()) {
     46 sd._serverId = "weblogic";
     47 } else if (isWebSphere()) {
     48 sd._serverId = "websphere";
     49 }
     50 if (isJetty()) {
     51 if (sd._serverId == null) {
     52 sd._serverId = "jetty";
     53 } else {
     54 sd._serverId += "-jetty";
     55 }
     56 } else if (isTomcat()) {
     57 if (sd._serverId == null) {
     58 sd._serverId = "tomcat";
     59 } else {
     60 sd._serverId += "-tomcat";
     61 }
     62 }
     63 if (sd._serverId == null) {
     64 throw new RuntimeException("Server is not supported");
     65 }
     66 }
     67 return sd._serverId;
     68 }
     69 
     70 public static boolean isGeronimo() {
     71 ServerDetector sd = _instance;
     72 if (sd._geronimo == null) {
     73 sd._geronimo = _detect("/org/apache/geronimo/system/main/Daemon.class");
     74 }
     75 return sd._geronimo.booleanValue();
     76 }
     77 
     78 public static boolean isGlassfish() {
     79 ServerDetector sd = _instance;
     80 if (sd._glassfish == null) {
     81 String value = System.getProperty("com.sun.aas.instanceRoot");
     82 if (value != null) {
     83 sd._glassfish = Boolean.TRUE;
     84 } else {
     85 sd._glassfish = Boolean.FALSE;
     86 }
     87 }
     88 return sd._glassfish.booleanValue();
     89 }
     90 
     91 public static boolean isJBoss() {
     92 ServerDetector sd = _instance;
     93 if (sd._jBoss == null) {
     94 sd._jBoss = _detect("/org/jboss/Main.class");
     95 }
     96 return sd._jBoss.booleanValue();
     97 }
     98 
     99 public static boolean isJetty() {
    100 ServerDetector sd = _instance;
    101 if (sd._jetty == null) {
    102 sd._jetty = _detect("/org/mortbay/jetty/Server.class");
    103 }
    104 return sd._jetty.booleanValue();
    105 }
    106 
    107 public static boolean isJOnAS() {
    108 ServerDetector sd = _instance;
    109 if (sd._jonas == null) {
    110 sd._jonas = _detect("/org/objectweb/jonas/server/Server.class");
    111 }
    112 return sd._jonas.booleanValue();
    113 }
    114 
    115 public static boolean isOC4J() {
    116 ServerDetector sd = _instance;
    117 if (sd._oc4j == null) {
    118 sd._oc4j = _detect("oracle.oc4j.util.ClassUtils");
    119 }
    120 return sd._oc4j.booleanValue();
    121 }
    122 
    123 public static boolean isResin() {
    124 ServerDetector sd = _instance;
    125 if (sd._resin == null) {
    126 sd._resin = _detect("/com/caucho/server/resin/Resin.class");
    127 }
    128 return sd._resin.booleanValue();
    129 }
    130 
    131 public static boolean isSupportsComet() {
    132 return false;
    133 }
    134 
    135 public static boolean isTomcat() {
    136 ServerDetector sd = _instance;
    137 if (sd._tomcat == null) {
    138 sd._tomcat = _detect("/org/apache/catalina/startup/Bootstrap.class");
    139 }
    140 if (sd._tomcat == null) {
    141 sd._tomcat = _detect("/org/apache/catalina/startup/Embedded.class");
    142 }
    143 return sd._tomcat.booleanValue();
    144 }
    145 
    146 public static boolean isWebLogic() {
    147 ServerDetector sd = _instance;
    148 if (sd._webLogic == null) {
    149 sd._webLogic = _detect("/weblogic/Server.class");
    150 }
    151 return sd._webLogic.booleanValue();
    152 }
    153 
    154 public static boolean isWebSphere() {
    155 ServerDetector sd = _instance;
    156 if (sd._webSphere == null) {
    157 sd._webSphere = _detect("/com/ibm/websphere/product/VersionInfo.class");
    158 }
    159 return sd._webSphere.booleanValue();
    160 }
    161 
    162 private static Boolean _detect(String className) {
    163 try {
    164 ClassLoader.getSystemClassLoader().loadClass(className);
    165 return Boolean.TRUE;
    166 } catch (ClassNotFoundException cnfe) {
    167 ServerDetector sd = _instance;
    168 
    169 Class<?> c = sd.getClass();
    170 if (c.getResource(className) != null) {
    171 return Boolean.TRUE;
    172 }
    173 }
    174 return Boolean.FALSE;
    175 }
    176 }
    复制代码

        OK ,到此完美解决了 Spring MVC 中zip包下载、避开后端直接写入前端下载流、妥善解决各web容器的差异性。

    作者:Orson 
    出处:http://www.cnblogs.com/java-class/ 
    如果,您认为阅读这篇博客让您有些收获,不妨点击一下右下角的【推荐】 
    如果,您希望更容易地发现我的新博客,不妨点击一下左下角的【关注我】 
    如果,您对我的博客内容感兴趣,请继续关注我的后续博客,我是【Orson】 

    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段 声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。 

    转载:http://www.cnblogs.com/java-class/p/4798204.html

    展开全文
  • 还可以从action方法签名实现转换 - 25 - 高级HTTP Java绑定 - 26 - 简单类型 - 26 - Date类型 - 26 - Calendar日历 - 27 - File - 27 - 支持类型的数组或集合 - 28 - POJO对象绑定 - 29 - JPA 对象绑定 - 30 - 定制...
  •  有时候随笔比较长,会分多个部分,此时需要像 word目录那样,点击目录跳转到对应位置这样的功能,锚点(如下图箭头所指位置)可以实现该需求。   方法:  以 TinyMCE编辑器 为例  选中你要跳转...

    参考:https://q.cnblogs.com/q/41571/     Jacklovely  | 2018-05-30 11:23  的回答

    需求:

      有时候随笔比较长,会分多个部分,此时需要像 word 里目录那样,点击目录名跳转到对应位置这样的功能,锚点(如下图箭头所指位置)可以实现该需求。

      

     

    方法:

      以 TinyMCE编辑器 为例

      选中你要跳转到的文字,点击锚图标,输入书签名字,要用英文或数字,且是英文开头,比如“maodian_1”,(可以用下划线  不可以用空格)  。

      

      这时文字前面就会出现一个锚点的图标。

      

      然后选中你的目录的文字,点击链接,“锚点”下拉框选择“mark1”,“目标”下拉框选择“在当前窗口打开超链接”。  默认是新窗口打开~

      

     

      

     

    示例:

    目录1

    目录2

     

    目录1对应锚点

     

     

     

     

    目录1对应锚点

     

    转载于:https://www.cnblogs.com/mumu122GIS/p/10647193.html

    展开全文
  • 输入指定页码,点击定位即可实现跳转;也可以输入+几/-几,来达到翻多页的效果 word 打开word,点击左下角的页码,(我的是word2019,太老的版本可能不适用) 左侧导航中即可实现快速翻页 图片题注 对文档中的图片...
  • 闪电pdf阅读器是一款好用的PDF阅读软件,内存消耗小,极速打开pdf文件,具有多种极速阅读模式,...新增了PDF的转换功能,用户可以在软件内直接将PDF转换成word,轻松实现文档间的转换,提高工作效率 闪电pdf阅读器截图
  • 站长助手(WEBMASTER HELPER) v2.0,该源码实现的很不错,该功能简介:文件管理:可以浏览某目录下的文件和目录,并对其进行操作(编辑,删除,打包,解压等),还可以进行定义操作。支持多文件上传,新建目录和文件。...
  • 实例190 通过ADO方式实现输入页码跳转到指定页 297 实例191 通过函数实现单击页码跳转到指定页 300 实例192 添加图书类的实现 302 第8章 SQL查询相关技术 305 8.1 数据库操作 306 实例193 创建数据库 ...
  • 实例190 通过ADO方式实现输入页码跳转到指定页 297 实例191 通过函数实现单击页码跳转到指定页 300 实例192 添加图书类的实现 302 第8章 SQL查询相关技术 305 8.1 数据库操作 306 实例193 创建数据库 ...
  • 基于“微信小程序” + “云开发”实现的单词对战小程序,支持好友对战、随机匹配、人机对战三种对战的形式,含四级核心词、四级大纲词、六级核心词、六级大纲词、考研真题核心词、考研大纲词、小学必备词、中考大纲...
  • 内容包括HTML语句的基本语法,JSP指令与动作,JSP内部对象,JavaScript脚本在JSP编程中的使用,JavaBean在JSP中的基本概念及其属性与方法,数据库的连接方式,绘图方法,实现邮件的收发功能,打印Word、Excel和PDF...
  • delphi 开发经验技巧宝典源码

    热门讨论 2010-08-12 16:47:23
    0027 使用GoTo跳转语句 21 0028 有效使用Case Else语句 22 0029 保证数组循环的安全性 22 0030 获取枚举值列表 23 0031 使两个变量位于同一个地址空间 23 0032 利用动态数组作为函数参数 24 0033 调用...
  • 0027 使用GoTo跳转语句 21 0028 有效使用Case Else语句 22 0029 保证数组循环的安全性 22 0030 获取枚举值列表 23 0031 使两个变量位于同一个地址空间 23 0032 利用动态数组作为函数参数 24 0033 调用...
  • 0027 使用GoTo跳转语句 21 0028 有效使用Case Else语句 22 0029 保证数组循环的安全性 22 0030 获取枚举值列表 23 0031 使两个变量位于同一个地址空间 23 0032 利用动态数组作为函数参数 24 0033 调用...
  • 0027 使用GoTo跳转语句 21 0028 有效使用Case Else语句 22 0029 保证数组循环的安全性 22 0030 获取枚举值列表 23 0031 使两个变量位于同一个地址空间 23 0032 利用动态数组作为函数参数 24 0033 调用...
  • 0027 使用GoTo跳转语句 21 0028 有效使用Case Else语句 22 0029 保证数组循环的安全性 22 0030 获取枚举值列表 23 0031 使两个变量位于同一个地址空间 23 0032 利用动态数组作为函数参数 24 0033 调用...
  • 0027 使用GoTo跳转语句 21 0028 有效使用Case Else语句 22 0029 保证数组循环的安全性 22 0030 获取枚举值列表 23 0031 使两个变量位于同一个地址空间 23 0032 利用动态数组作为函数参数 24 0033 调用...
  • 2018年4月迭代计划

    2021-01-06 15:50:14
    #484</li><li>[x] 非超级管理员可以运行升级向导 #485</li><li>[x] 在地址栏输入没有的.aspx文件会自动跳转到后台目录的错误页,暴露后台地址 #485</li><li>[x] 后台的文章搜索没有标题选项 #494</li><li>[x]...
  • C#编程经验技巧宝典

    热门讨论 2008-06-01 08:59:33
    C#编程经验技巧宝典源代码,目录如下: 第1章 开发环境 1 <br>1.1 Visual Studio开发环境安装与配置 2 <br>0001 安装Visual Studio 2005开发环境须知 2 <br>0002 配置合适的Visual Studio 2005...
  •  上述代码即响应用户F1按键,若当前显示界面设置了热点敏感帮助的页面ID时,程序将打开CHM帮助文档并跳转到ID对应的页面;若当前显示界面没有设置热点敏感帮助的页面ID,则显示软件帮助缺省的页面。IDH_WJH_...
  • 实例104 使用Redirect方法实现页面跳转并传递参数 165 实例105 使用Response对象设置页面缓存 166 实例106 BinaryWrite方法输出二进制图像 167 4.2 Request对象:程序请求对象 169 实例107 获取客户端IP地址 169 ...
  • 实例104 使用Redirect方法实现页面跳转并传递参数 165 实例105 使用Response对象设置页面缓存 166 实例106 BinaryWrite方法输出二进制图像 167 4.2 Request对象:程序请求对象 169 实例107 获取客户端IP地址 169 ...
  • 实例104 使用Redirect方法实现页面跳转并传递参数 165 实例105 使用Response对象设置页面缓存 166 实例106 BinaryWrite方法输出二进制图像 167 4.2 Request对象:程序请求对象 169 实例107 获取客户端IP地址 169 ...
  • 由于HLP文件所支持的格式只能是RTF,所以用户在使用HW之前必须先用WORD制作好RTF文件。 二、制作前的准备 1、下载并安装 HTML Help WorkShop 你可以在微软的站点上下载 HHW 软件,也可以直接 按此下载...
  • 目录(善用Ctrl+F) 基础入门 新手 Golang开发新手常犯的50个错误 数据类型 连nil切片和空切片一不一样都不清楚?那BAT面试官只好让你回去等通知了。 golang面试题:字符串转成byte数组,会发生内存拷贝吗? ...

空空如也

空空如也

1 2 3
收藏数 52
精华内容 20
关键字:

word目录实现跳转