大家分享一款网页书签管理工具,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 插件。
功能特色:
项目地址:
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 实现,支持按需加载各种插件包括:
MovieLine - 电影经典台词: 随机一句电影经典台词,并展示其电影海报作为背景,支持动态设置各种显示。
Bookmark - 书签: 书签管理器,当前文件夹只支持一级目录。
推荐的开源项目已经收录到 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客户端
*请认真填写需求信息,我们会在24小时内与您取得联系。