整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

URL Manager Pro Mac版(网页书签管

URL Manager Pro Mac版(网页书签管理工具)

大家分享一款网页书签管理工具,URL Manager Pro Mac版支持Firefox、Vivaldi、Opera、Chrome、Safari等各种浏览器,你可以轻松导入和导出浏览器的书签,更便于用户管理书签,功能很实用,有需要书签管理器的朋友可以试试URL Manager Pro mac版哦!

URL Manager Pro for mac官方介绍

URL Manager Pro是Safari,Chrome,Chromium,Edge,Vivaldi,Opera,iCab和Firefox浏览器的专业书签管理器,使书签管理变得更加容易。

这是1996年以来的经典版本,但现在已改写为64位应用程序。

https://www.mac69.com/mac/9704.html

URL Manager Pro mac版功能特点

- 系统范围的访问:您可以通过系统菜单在任何应用程序中访问书签。

- 集成:您可以从浏览器的菜单栏和macOS Dock中访问书签。

- 文档存储:您的书签存储在URL Manager Pro书签文档中,您可以用普通的Mac方式存储和操作它们。无需在浏览器的首选项文件夹中寻找书签。您可以进行自动定期保存,并在每次保存时在备份设备上制作书签文档的副本。

- 历史记录:您可以使用几种不同的浏览器浏览Web,而URL Manager Pro会继续记录每个浏览器的历史记录。

- 导入和导出书签:“文件”菜单为您提供URL Manager Pro可以导入和导出到的所有浏览器的列表。通过扫描HTML和文本文件中的书签和URL,它可以让您“收获”书签。

- 它使您可以快速编辑和组织书签:您可以使用内联编辑,为书签添加标签和上色。

家好,我是 Java陈序员。

浏览器是我们上网冲浪的必备工具,每次打开浏览器默认都是先看到起始页。

有的浏览器起始页十分简洁美观,而有的则是充满了各种网址导航和广告。

今天,給大家介绍一个浏览器起始页配置插件,支持自定义配置。

关注微信公众号:【Java陈序员】,获取开源项目分享、AI副业分享、超200本经典计算机电子书籍等。

项目介绍

Howdz Dashboard —— 一个基于 Vue3、Typescript、Vite 的完全自定义配置的浏览器起始页,支持 Chrome 插件和 Edge 插件。

功能特色:

  • 提供基于文档流的响应布局或 Fixed 模式固定任意位置的两种布局模式
  • 响应式设计,编辑模式下拖拽组件更改其大小和位置
  • 组件可自由添加或删除,并且组件的样式和功能都是可配置的
  • 数据可使用密钥或 JSON 文件导出以实现数据同步功能
  • 预设多种主题,初始进入可任意选择
  • 支持本地壁纸、随机壁纸,随机壁纸支持收藏个人壁纸库
  • 可配置动态壁纸背景,选用官方提供或自行添加网络视频路径即可
  • 可配置多标签页模式,允许添加切换多个隔离的页面
  • 可配置组件交互行为,设置点击组件弹出另一个组件等交互操作
  • 预览模式, 支持用于分享用的数据隔离模式
  • 支持语言切换,当前支持简体中文与英文

项目地址:

https://github.com/leon-kfd/Dashboard

在线体验地址:

https://www.howdz.xyz/

功能体验

预设多种主题,初始进入可任意选择。

可动态设置壁纸,支持纯色壁纸、本地图片壁纸、网络图片壁纸、随机图片壁纸,随机图片壁纸支持收藏个人壁纸库。

Tips:左下角的图标按钮可以更新壁纸和收藏壁纸。

壁纸还支持多种动画特效。

可以从物料组件库自行添加自己需要的组件,添加的组件提供很多样式和功能的配置进行修改,并通过拖拽更改组件位置和大小。

提供大量组件用于定制化你的起始页,可适配响应式设计。

物料组件

Howdz Dashboard 提供大量组件用于定制化起始页,这里挑选几个比较有趣的组件。

Empty - 占位: 占位区块组件,支持一些简单配置与自定义文本。

Day - 自定义日期: 基于 Dayjs 的 formatter 格式化占位符语法实现自定义各种日期格式。

Verse - 随机古诗: 随机古诗组件,API 来源于 https://www.jinrishici.com/, 可配置定时刷新。

Search - 搜索栏: 支持添加自定义搜索引擎、按 Tab 键快速切换搜索引擎、支持关键词联想。

Collection - 键盘收藏夹: 键盘收藏夹,设置网站后按相应按键自动跳转,网站 Icon 自动获取。

Iframe - 外部网站: 设置嵌入 Iframe,最新版浏览器只支持同协议(当前网站为 https)的 Iframe.

TodoList - 备忘清单: 可同时设置不同日期,点击上方日期展开日期选择器。

Weather - 天气: 天气组件,支持通过 IP 自动获取城市也可手动输入,后续考虑添加读取 GPS。

CountDown - 倒计时: 支持天、小时、分钟三种单位的设置倒计时事件。

WeiboList - 微博热搜: 显示最新微博热搜列表,支持配置自动刷新。

此外,还有掘金热门、Github 趋势、知乎热榜。

Editor - Markdown编辑器: 基于 Milkdown 实现,支持按需加载各种插件包括:

  • Tooltip: 在选择文本后会在其上方出现工具栏可快速切换格式
  • Slash: 斜线命令,输入“/”后会弹出快速选择工具栏
  • Clipboard: 为编辑器添加复制粘贴 Markdown 格式功能
  • History: 增加历史记录功能,使用 Ctrl+Z 和 Ctrl+Y
  • Prism: 为代码块增加高亮功能。

MovieLine - 电影经典台词: 随机一句电影经典台词,并展示其电影海报作为背景,支持动态设置各种显示。

Bookmark - 书签: 书签管理器,当前文件夹只支持一级目录。

  • 添加时输入网址可自动获取网站 ICON 与标题
  • 图标样式、大小、背景色都可自定义,图标可缓存为 Base64
  • 支持从 Chrome 内核的浏览器导出的书签 HTML 文件导入

最后

推荐的开源项目已经收录到 GitHub 项目,欢迎 Star:

https://github.com/chenyl8848/great-open-source-project

或者访问网站,进行在线浏览:

https://chencoding.top:8090/#/

大家的点赞、收藏和评论都是对作者的支持,如文章对你有帮助还请点赞转发支持下,谢谢!

章最后提供源码下载地址

市面上处理文字的的办公软件有很多,包括WPS、MSOffice、永中OFFICE,当然还有开源的openoffice、liboffice等。我们在项目开发过程中经常会遇到预览word文件,数据库中数据自动填充word模板等需求。现在能够满足以上需求的技术有很多,服务端可通过POI\aspose等处理,也可通过客户端调用OFFICE组件处理,本人曾经在这方便做了很多测试,最终发现兼容性最好的、接口对JAVA程序员最友好的就属永中OFFICE,因为它基本就是JAVA实现的,使用起来非常方便。

我的测试环境使用的是永中2016版本,它运行要求JRE1.6,且我发现它应该是对JRE进行过重构,按永中SDK要求编写代码通过自ORACAL官网下载的jdk1.6编译后运行是失败的,现在都2021年了,我们的项目绝大多数都JDK1.8以上版本了,那么怎么让SDK兼容我们的项目呢?怎么实现标题中提到的两个需求呢?下面我说说我的处理方法吧:

1、下载永中软件并安装(官网下载即可)

2、安装后打开安装路径可以看到如下图

永中软件安装目录

JRE:即永中软件的运行环境

Yozo_Office.jar: 即永中为开发者提供的SDK,可以将jar导入到工程中

3、编写WORD文件处理服务组件

处理word文件的代码片段,详细代码请在文后下载源码查阅	

/**
	 * 将word文件转换为对应格式的文件的字节数组
	 * @param type 将word文件转换成的文件格式 pdf、html\ofd\txt\xml
	 * @return
	 * @throws IOException
	 */
	public byte[]  convertFile(String type) throws IOException {
		int typePdf=FileConstants.TYPE_PDF;
		if("html".equals(type.toLowerCase())) {//此功能转换后乱码,后期可采用 this.workbook.saveAs("D:/2.html"); 方式存储html后,将字节返回
			typePdf=FileConstants.FILETYPE_HTML;
		}else if("ofd".equals(type.toLowerCase())) {
			typePdf=FileConstants.TYPE_OFD;	// 这个是不成功的,应该是版本太低
		}else if("txt".equals(type.toLowerCase())) {
			typePdf=FileConstants.TYPE_TXT;
		}else if("xml".equals(type.toLowerCase())) {
			typePdf=FileConstants.FILETYPE_XML;
		}else if("doc".equals(type.toLowerCase())||"xls".equals(type.toLowerCase())||"ppt".equals(type.toLowerCase())) {
			typePdf=FileConstants.TYPE_MS;
		}else if("docx".equals(type.toLowerCase())||"xlsx".equals(type.toLowerCase())||"pptx".equals(type.toLowerCase())) {
			typePdf=FileConstants.TYPE_MS_EX;
		}
		
		return this.workbooks.getWorkbookAsByteArray(workbook, typePdf);
	}

/**
	 * 替换word模板中的书签
	 * @param jsonObject	数据内容 {“bookmarkname”:”test“}
	 */
	public void replaceBookMark(JSONObject jsonObject) {
	    BookMarks bookMarks=this.document.getBookMarks();
        BookMark[] allBookmarks=bookMarks.getAllBookmarks();
		for(BookMark bookMark:allBookmarks){
            String name=bookMark.getName();
            TextRange range=bookMark.getRange();
            //if(name!=null)name=name.replace("PO_","");
            String value="";
            Object o=jsonObject.get(name);
            if(o!=null){
                value=jsonObject.get(name).toString();
            }
            try { 
                    range.insertText(value);               
            }catch (Exception e){
                range.insertText(value);
            }
		}
	}

/**
	 * 导出数据成excel文件
	 * @param jsonObject	数据内容 {“bookmarkname”:”test“}
	 */
public byte[] exportData2File(JSONArray taskArray,int allrow) {

}

4、(重点)解决word文件处理组件与我们的项目文件交互问题

本人通过SOCKET即时通讯服务解决数据交互问题

/**
 * 文件传输Server端<br>
 * 功能说明:
  * @Author 空中智囊
 * @Date 2016年09月01日
 * @version 1.0
 */
public class SocketService extends ServerSocket {
    private static final int SERVER_PORT=8899; // 服务端端口

    private WordUtil wordUtil=null;
    public SocketService() throws Exception {
        super(SERVER_PORT);
        this.wordUtil=new WordUtil();
    }

    /**
     * 使用线程处理每个客户端传输的文件
     * @throws Exception
     */
    public void load() throws Exception {
        System.out.println("服务端启动,监听端口为:"+SERVER_PORT);
        while (true) {
            // server尝试接收其他Socket的连接请求,server的accept方法是阻塞式的
            Socket socket=this.accept();
            socket.setSoTimeout(1200000);
            /**
             * 我们的服务端处理客户端的连接请求是同步进行的, 每次接收到来自客户端的连接请求后,
             * 都要先跟当前的客户端通信完之后才能再处理下一个连接请求。 这在并发比较多的情况下会严重影响程序的性能,
             * 为此,我们可以把它改为如下这种异步处理与客户端通信的方式
             */
            // 每接收到一个Socket就建立一个新的线程来处理它
            new Thread(new Task(socket,wordUtil)).start();

        }
    }
    /**
     * 入口
     * @param args
     */
    public static void main(String[] args) {
        try {
            SocketService server=new SocketService(); // 启动服务端
            server.load();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
/**
 * 处理客户端传输过来的文件线程类
 */
public class Task implements Runnable {
  @Override
    public void run() {
        System.out.println("===客户端连接成功=====");

        System.out.println("****************************************************************");
        SimpleDateFormat format=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        /**
         * 转换要求的格式
         */
        try {

            /********************************读取文件信息********************************/
            dis=new DataInputStream(socket.getInputStream());
            // 文件名和长度
            String fileName=dis.readUTF();//1、文件名字
            long fileLength=dis.readLong();//2、长度
            String toext=dis.readUTF();//3、扩展名
            String taskType=dis.readUTF();//4、文件操作类型
            System.out.println("针对文件的操作类型====="+taskType);
            String valueObject=dis.readUTF();//5、替换书签的值
            System.out.println(format.format(new Date())+":开始接收文件");
            ByteArrayOutputStream bos=new ByteArrayOutputStream((int)fileLength);
            byte[] bytes=new byte[1024];
            int length=0;
            while((length=dis.read(bytes, 0, bytes.length)) !=-1) {
                bos.write(bytes, 0, length);
            }
            byte[] filebytes=bos.toByteArray();
            System.out.println("原始文件大小====="+fileLength+",实际接收文件大小="+filebytes.length);

            /********************************读取文件信息结束********************************/

            dos=new DataOutputStream(socket.getOutputStream());

            /********************************校验文件信息********************************/
            boolean process=true;
            if(fileLength>0){
            }else{
                dos.writeUTF("error");
                dos.flush();

                dos.writeUTF("文件没有任何内容,请重新传送");
                dos.flush();
                process=false;
            }
            if(filebytes.length!=fileLength){
                dos.writeUTF("error");
                dos.flush();

                dos.writeUTF("接受文件与实际文件大小不符合,请重新传送文件");
                dos.flush();
                process=false;
            }
            /********************************校验文件信息结束********************************/

            /********************************处理文件********************************/
            if(process){
                byte[] fileBytes=null;
                
                this.wordUtil.openFile(filebytes,fileName);//打开院文件
                //workbook=workbooks.createWorkbookFromByteArray(filebytes,fileName);
                String lowerExt=toext.toLowerCase();
                if("convertFile".equals(taskType)){
                    System.out.println("开始将文件["+fileName+"]转换成===="+lowerExt);
                    fileBytes=this.wordUtil.convertFile(lowerExt);
                    System.out.println(format.format(new Date())+":转换"+toext+"完成");
                }else if("replaceBookMark".equals(taskType)){
                    System.out.println("开始将文件["+fileName+"]书签进行替换====");
                    JSONObject jsonObject=JSONObject.fromObject(valueObject);
                    this.wordUtil.replaceBookMark(jsonObject);
                    fileBytes=this.wordUtil.convertFile(lowerExt);
                    System.out.println("===============替换书签完成============");
                }else if("exportTask".equals(taskType)) {//处理业务数据 导出任务数据
                	System.out.println("开始导出业务数据===="+valueObject);
                	ServiceUtil serviceUtil=new ServiceUtil(this.wordUtil);
                	JSONObject jsonObject=JSONObject.fromObject(valueObject);
                	fileBytes=serviceUtil.exportData2File(jsonObject.getJSONArray("datalist"), jsonObject.getInt("size"));
                	 System.out.println("===============导出业务数据完成============");
                }
                
                
                /********************************处理文件结束********************************/
                if(fileBytes==null){
                    dos.writeUTF("error");
                    dos.flush();
                    dos.writeUTF("处理文件过程中错误");
                    dos.flush();
                    process=false;
                }


                /********************************返回处理过的文件********************************/
                if(process){
                    dos.writeUTF("info");//文件处理完成,将信息返回到客户端
                    dos.flush();
                    int fileBytelength=fileBytes.length;//转换后的文件长度
                    System.out.println(format.format(new Date())+":========服务端开始发送文件流,文件大小("+getFormatFileSize(fileBytelength)+")========");
                    dos.writeLong(fileBytelength);
                    dos.flush();
                    dos.write(fileBytes, 0, fileBytelength);//将文件一起写入到输出流发送
                    dos.flush();
                    System.out.println(format.format(new Date())+":========发送文件流成功========");
                }
                /********************************返回处理过的文件完成********************************/

            }
        } catch (Exception e) {
            String error=e.toString();
            System.out.println("error==================="+error);
            StackTraceElement[] stackTrace=e.getStackTrace();
            for(StackTraceElement s:stackTrace){
                int lineNumber=s.getLineNumber();
                String methodName=s.getMethodName();
                String className=s.getClassName();
                String filename=s.getFileName();
                System.out.print("err:"+filename+"  "+className+"  "+methodName+"  "+lineNumber);
                System.out.println("");
            }
            try {
                dos.writeUTF("error");
                dos.flush();
                dos.writeUTF("处理文件过程中错误=="+e.toString());
                dos.flush();
            }catch (Exception ex){
                String exrror=ex.toString();
                System.out.println("返回数据处理错误信息==================="+exrror);
            }

        }finally {
            System.out.println("关闭资源");
            try {
                if(wordUtil!=null)wordUtil.close();
                socket.close();
            } catch (Exception e) {
                String error=e.toString();
                System.out.println(error);
                e.printStackTrace();
            }
            System.out.println("****************************************************************");
        }
}


/**
 * 文件传输Clinet端<br>
 * 功能说明:
 * @Author 空中智囊
 * @Date 2016年09月01日
 * @version 1.0
 */
public class SocketClient extends Socket {
    public static final Logger LOGGER=LoggerFactory.getLogger(SocketClient.class);

    private static final String SERVER_IP="127.0.0.1"; // word文件组件处理服务IP地址
    private static final int SERVER_PORT=8899;	// word文件组件处理服务端口
    private int soTimeout=60000;	// 服务链接超时时间 60s
    private Socket client=this;
    private FileInputStream fis;
    private DataOutputStream dos;
    private DataInputStream dis;
    private FileOutputStream fos;

    public SocketClient(String listenip, int listenport) throws Exception {
        super(listenip, listenport);
        this.setSoTimeout(this.soTimeout);
        LOGGER.info("Cliect[port:" + this.client.getLocalPort() + "] 成功连接服务端");
    }

    public SocketClient() throws Exception {
        super(SERVER_IP, SERVER_PORT);
        this.setSoTimeout(this.soTimeout);
        LOGGER.info("Cliect[port:" + this.client.getLocalPort() + "] 成功连接服务端");
    }

    public SocketClient(String listenip, int listenport, int soTimeout) throws Exception {
        super(listenip, listenport);
        this.setSoTimeout(soTimeout);
        LOGGER.info("Cliect[port:" + this.client.getLocalPort() + "] 成功连接服务端");
    }

    /**
     * 处理word文件
     * @param srcRealPath   模板word文件路径绝对地址
     * @param descRealPath  处理后的文件存放地址绝对路径
     * @param taskType      处理文件的类型 convertFile/replaceBookMark/exportTask 
     * @param jsonObject    传给服务端的数据对象,这个参数可根据服务端需求进行调整
     * @return  处理结果
     */
public JSONObject processOffice(String srcRealPath, String descRealPath, String taskType, JSONObject jsonObject) {
        JSONObject rtnObject=new JSONObject();
        String code="200";
        String message="";

        try {
            File file=new File(srcRealPath);
            if (!file.exists() || !file.canWrite()) {
                code="200";
                message="文件不存在,或已被占用";
                rtnObject.element("code", code);
                rtnObject.element("message", message);
                JSONObject var41=rtnObject;
                return var41;
            }

            LOGGER.info(srcRealPath + "===>" + descRealPath);
            if (file.exists() && file.canWrite()) {
                String filename=file.getName();
                this.fis=new FileInputStream(file);
                this.dos=new DataOutputStream(this.client.getOutputStream());
                this.dos.writeUTF(filename);//文件名字
                this.dos.flush();
                this.dos.writeLong(file.length());//文件长度
                this.dos.flush();
                String ext=descRealPath.substring(descRealPath.lastIndexOf(".") + 1, descRealPath.length());
                this.dos.writeUTF(ext);//源文件后缀名字
                this.dos.flush();
                this.dos.writeUTF(taskType);//任务类型
                this.dos.flush();
                if (YOZOOfficeUtil.PROCESS_TYPE_CONVERTFILE.equals(taskType)) {
                    this.dos.writeUTF(jsonObject.toString());
                    this.dos.flush();
                }

                LOGGER.info("========开始向服务端传送源文件" + srcRealPath + "========");
                byte[] bytes=new byte[1024];
                long progress=0L;

                int length;
                while((length=this.fis.read(bytes, 0, bytes.length)) !=-1) {
                    this.dos.write(bytes, 0, length);
                    this.dos.flush();
                    progress +=(long)length;
                    LOGGER.info("| " + 100L * progress / file.length() + "% |");
                }

                LOGGER.info("========文件传输成功 (" + file.length() / 1048576L + ")M========");
                this.client.shutdownOutput();
                LOGGER.info("========开始转换" + ext + "========");
                InputStream inputStream=this.client.getInputStream();
                this.dis=new DataInputStream(inputStream);
                String result=this.dis.readUTF();
                if ("error".equals(result)) {
                    String reason=this.dis.readUTF();
                    LOGGER.info(reason);
                    code="500";
                    message=reason;
                } else if ("info".equals(result)) {
                    long l=this.dis.readLong();
                    LOGGER.info("========转换" + ext + "完成,文件大小(" + l / 1048576L + ")M========");
                    LOGGER.info("========开始接受" + ext + "========");
                    File newFile=new File(descRealPath);
                    if (newFile.exists()) {
                        newFile.delete();
                    }

                    this.fos=new FileOutputStream(newFile);
                    progress=0L;
                    bytes=new byte[1048576];

                    while((length=this.dis.read(bytes, 0, bytes.length)) !=-1) {
                        this.fos.write(bytes, 0, length);
                        this.fos.flush();
                    }

                    LOGGER.info("========接受" + ext + "文件成功========");
                    this.dis.close();
                } else {
                    code="500";
                    message="链接被强制关闭....";
                }
            } else {
                code="404";
                message="文件不存在,或已被占用:" + srcRealPath;
            }
        } catch (Exception e) {
            code="500";
            message="客户端报错:" + e.toString();
            LOGGER.error("异常:",e);

        } finally {
            if (this.fis !=null) {
                try {
                    this.fis.close();
                } catch (Exception var38) {
                    ;
                }
            }

            if (this.fos !=null) {
                try {
                    this.fos.close();
                } catch (Exception var37) {
                    ;
                }
            }

            try {
                this.client.close();
            } catch (Exception var36) {
                ;
            }

        }

        rtnObject.element("code", code);
        rtnObject.element("message", message);
        return rtnObject;
    }

    public static void main(String[] args) {
        try {
            SocketClient socketClient=new SocketClient();
            // 将文档转换成pdf文件
            socketClient.processOffice("D:/2.doc","D:/2.pdf",YOZOOfficeUtil.PROCESS_TYPE_CONVERTFILE,null);

            // 将文档转换成pdf文件
            JSONObject dataObject=new JSONObject();
            dataObject.element("bookmarkname","这个是测试呢日哦那个");
            socketClient.processOffice("D:/2.doc","D:/2.pdf",YOZOOfficeUtil.PROCESS_TYPE_REPLACEBOOKMARK,dataObject);
        } catch (Exception e) {
            LOGGER.error("异常:",e);

        }

    }
}

5、启动word文件处理组件服务端

组件启动脚本

nohup ./ofdServer.sh &


6、调用服务端对word文件处理

  public static void main(String[] args) {
        try {
            SocketClient socketClient=new SocketClient();
            // 将文档转换成pdf文件
            socketClient.processOffice("D:/2.doc","D:/2.pdf",YOZOOfficeUtil.PROCESS_TYPE_CONVERTFILE,null);

            // 替换模板中的书签值,word中插入书签自行百度
            JSONObject dataObject=new JSONObject();
            dataObject.element("bookmarkname","这个是测试呢日哦那个");
            socketClient.processOffice("D:/2.doc","D:/3.doc",YOZOOfficeUtil.PROCESS_TYPE_REPLACEBOOKMARK,dataObject);
        } catch (Exception e) {
            LOGGER.error("异常:",e);

        }

    }

7、资源下载

word文件处理组件服务端(开箱即用):

链接: https://pan.baidu.com/s/1_ZgjoX_nuv3a7_SKkJ_D7w 提取码: hn2r


服务端资源内容


将文件复制到linux服务器,并解压,执行 ./ofdServer.sh ,输出:服务端启动,监听端口为:8899,即运行成功

word文件处理组件客户端(开箱即用processOffice):

链接: https://pan.baidu.com/s/1mtabGY87RuAGGkwKrBIvfQ 提取码: mqxf

客户端资源文件内容

将源文件复制到项目指定包名,运行SocketClient.java中的main方法,可查看运行结果。

最重要的一点:服务器要安装永中OFFICE客户端