大家分享一款網頁書簽管理工具,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小時內與您取得聯系。