[Java] 根据Bookmarks.bak生成Chrome导入书签文件

场景:前几天我一个同事手误删了一整个文件夹的书签,还是没登录账号的情况下,也没点左下角的撤销按钮,不知道怎么恢复

网上有一种方法,是进到C:\\User\\AppData\\Local\\Google\\Chrome\\User Data\\Default目录下,将Bookmarks文件重命名为任意名称,将Bookmarks.bak文件去掉.bak后缀,然后重启浏览器就能恢复到.bak文件最后修改时间的书签内容

在同事电脑试了,没啥用,又因为同事当时需用电脑,暂时不能试重启是否有效

当时是直接在同事电脑上操作,我试过想看看Bookmarks文件里面是什么内容,但是双击后无法打开,系统是win10,后来我用自己电脑去试,双击能用文本编辑器打开,系统是win11

Bookmarks/Bookmarks.bak文件的内容结构是这样的:

{
  "checksum": "xxx",
  "roots": {
    "bookmark_bar": {
      "children": [
        {
          "date_added": "xxx",
          "date_last_used": "xxx",
          "guid": "xxx",
          "id": "xxx",
          "name": "百度一下,你就知道",
          "type": "url",
          "url": "https://www.baidu.com/"
        }
      ],
      "date_added": "xxx",
      "date_last_used": "0",
      "date_modified": "xxx",
      "guid": "xxx",
      "id": "xxx",
      "name": "书签栏",
      "type": "folder"
    },
    "other": {
      "children": [
        {
          "date_added": "xxx",
          "date_last_used": "xxx",
          "guid": "xxx",
          "id": "xxx",
          "name": "pixiv",
          "type": "url",
          "url": "https://www.pixiv.net/"
        }
      ],
      "date_added": "xxx",
      "date_last_used": "0",
      "date_modified": "xxx",
      "guid": "xxx",
      "id": "xxx",
      "name": "其他书签",
      "type": "folder"
    },
    "synced": {
      "children": [],
      "date_added": "xxx",
      "date_last_used": "0",
      "date_modified": "0",
      "guid": "xxx",
      "id": "xxx",
      "name": "移动设备书签",
      "type": "folder"
    }
  },
  "version": 1
}

结构很简单,属性也都是重复的,那就好办了~

直接改文件名的方式暂时看不到效果,那就只能根据这个json手动再录一遍呗~

咋可能手动录,咱是程序猿,咋可能去干这种费时费力的体力活!

写程序的终极目标是为了偷懒!

不想一个个录,那就批量导呗,咋导?

导出书签,再导入书签,不就行了,无非是内容不一样

那怎么批量改内容呢?

我的想法不是改内容,而是根据他的结构将Bookmarks文件的内容填充进去,生成一个新的导入书签文件

那导出书签的文件结构是怎样的呢

<!DOCTYPE NETSCAPE-Bookmark-file-1>
<!-- This is an automatically generated file.
     It will be read and overwritten.
     DO NOT EDIT! -->
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=UTF-8">
<TITLE>Bookmarks</TITLE>
<H1>Bookmarks</H1>
<DL><p>
    <DT><H3 ADD_DATE="xxx" LAST_MODIFIED="xxx" PERSONAL_TOOLBAR_FOLDER="true">书签栏</H3>
    <DL><p>
        <DT><A HREF="https://www.baidu.com/" ADD_DATE="xxx" ICON="data:image/png;base64,xxx">百度一下,你就知道</A>
    </DL><p>
    <DT><A HREF="https://www.pixiv.net/" ADD_DATE="xxx" ICON="data:image/png;base64,xxx">pixiv</A>
</DL><p>

诶,也很简单,最外层dl-p,文件夹用dt-h3然后再包一层dl-p放网址,网址则是dt-a

有迹可循,那就好办

以下是用Java编写实现读取Bookmarks.bak文件内容然后按照导入书签文件内容格式填充功能的代码

主类Main

import com.alibaba.fastjson.JSON;
// import helper.FileHelper;
// import model.BookmarksModel;
import org.apache.commons.lang3.StringUtils;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

/**
 * @author Canser
 * Bookmarks.bak是书签数据(Bookmarks)的备份文件,备份存在一定时差,故可作为短时间内误操作的书签还原
 * 短时间内指,误操作时间处于上一次备份和下一次备份的时间之内,如果.bak的修改时间已经超过误操作的时间,则于事无补
 * 注:出现误操作之后,千万不要再对书签进行操作,否则.bak会更新
 */
public class Main {

    public static void main(String[] args) {
        // 用户根目录
        String userDir = System.getProperty("user.home");
        // 这个路径是不会变动的
        String bookmarksPath = userDir + "\\AppData\\Local\\Google\\Chrome\\User Data\\Default\\Bookmarks.bak";
        File bookmarks = new File(bookmarksPath);
        // 获取文件内容
        String content = FileHelper.readFile(bookmarks);

        // 字符串转实体类,这里用的是fastjson
        BookmarksModel model = JSON.parseObject(JSON.parse(content).toString(), BookmarksModel.class);
        // 获取导入数据的html内容
        String html = getHtmlFromModel(model);
        // 创建和写入html中
        FileHelper.outputToFile(html);
    }

    private static String getHtmlFromModel(BookmarksModel model) {
        // 存放所有代码行
        List<String> list = new ArrayList<>();

        // 外层结构
        list.add("<!DOCTYPE NETSCAPE-Bookmark-file-1>");
        list.add("<META HTTP-EQUIV=\"Content-Type\" CONTENT=\"text/html; charset=UTF-8\">");
        list.add("<TITLE>Bookmarks</TITLE>");
        list.add("<H1>Bookmarks</H1>");

        // 书签栏数据
        getChildren(0, list, model.roots.bookmark_bar);
        // 其他书签数据
        getChildren(0, list, model.roots.other);

        // 拼接换行后返回
        return StringUtils.join(list, "\n");
    }

    private static void getChildren(int blankNum, List<String> list, BookmarksModel.Children... children) {
        // 前置空格,这个对象纯粹是为了html代码看的美观,实际没啥用
        String blankStr = " ".repeat(blankNum);
        // 最外层父级dl
        list.add(blankStr + "<DL><p>");
        // 前置空格数+4,即一个tab
        blankNum += 4;
        for (BookmarksModel.Children child : children) {
            if ("folder".equals(child.type)) {
                // 如果是文件夹
                list.add(String.format("%s<DT><H3 ADD_DATE=\"%s\" LAST_MODIFIED=\"%s\">%s</H3>",
                        " ".repeat(blankNum), child.date_added, child.date_last_used, child.name));
                if (child.children != null && child.children.length > 0) {
                    // 存在子文件夹则递归
                    getChildren(blankNum, list, child.children);
                }
            } else if ("url".equals(child.type)) {
                // 如果是地址
                list.add(String.format("%s<DT><A HREF=\"%s\" ADD_DATE=\"%s\">%s</A>",
                        " ".repeat(blankNum), child.url, child.date_added, child.name));
            }
            // 其实属性ADD_DATE和LAST_MODIFIED可以不放,毕竟导入后是属于新增的,这些参数都会被覆盖
        }
        list.add(blankStr + "</DL><p>");
    }

}

字符串映射实体类BookmarksModel

import lombok.Data;

/**
 * @author Canser
 * 实际用到的字段只有:
 * Roots.bookmark_bar、Roots.other、Children.date_added、Children.date_last_used、Children.name、Children.type、Children.url
 * 其他的字段和类其实是没用到的,可以看情况删除
 */
@Data
public class BookmarksModel {

    public String checksum;

    public Roots roots;

    public String version;

    @Data
    public static class Roots {

        public Children bookmark_bar;

        public Children other;

        public Children synced;

    }

    @Data
    public static class Children {

        public String date_added;

        public String date_last_used;

        public String guid;

        public String id;

        public String name;

        public String type;

        public String url;

        public MetaInfo meta_info;

        public Children[] children;

        @Data
        public static class MetaInfo {

            public String power_bookmark_meta;

        }

    }

}

文件帮助类FileHelper

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.util.ArrayList;

/**
 * @author Canser
 */
public class FileHelper {

    public static String readFile(File txt) {
        ArrayList<String> set = new ArrayList<>();
        if (!txt.exists()) {
            System.out.println("文件 " + txt.getName() + " 不存在!");
            return "";
        }
        try (BufferedReader reader = new BufferedReader(new FileReader(txt))) {
            String str = reader.readLine();
            while (str != null) {
                set.add(str);
                str = reader.readLine();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return String.join("\n", set.toArray(new String[0]));
    }

    public static void outputToFile(String content) {
        try {
            // 输出的文件路径自定义
            File outHtml = new File("src/main/resources/out.html");
            if (!outHtml.exists() && !outHtml.createNewFile()) {
                return;
            }
            try (FileWriter fileWriter = new FileWriter(outHtml)) {
                fileWriter.write(content);
                fileWriter.flush();
            } catch (Exception e) {
                e.printStackTrace();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

必要的Maven依赖项

        <!-- lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.28</version>
        </dependency>

        <!-- fastjson -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.54</version>
        </dependency>

        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.12.0</version>
        </dependency>

其中commons-lang3这个包,这里面只用到了StringUtils.join这一个方法,我为了图省事一句话解决就把这个包引进来了,一开始我是用StringBuilder的,但是这样的话每一行字符串后面都得手动加个\n,不太雅观,就换join拼接了,各位可以根据自己情况调整

FileHelper这个帮助类里我没怎么写判断,各位也可根据实际情况自行添加

运行main方法后找到生成的html文件,然后在chrome导入书签,再稍微调整一下位置就可以了(chrome导入会归到一个“已导入”的文件夹)

(这个代码生成的结构和导出书签的结构有略微区别,区别在于“其他书签”一栏,我没放到最外层的dl-p里,而是另起了一个dl-p,不过不影响,导入结束后拖一下位置就行了)

Bookmarks.bak是书签数据(Bookmarks)的备份文件,备份存在一定时差,故可作为短时间内误操作的书签还原
短时间内指,误操作时间处于上一次备份和下一次备份的时间之内,如果.bak的修改时间已经超过误操作的时间,则于事无补
注:出现误操作之后,千万不要再对书签进行操作,否则.bak会更新

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
使用Java生成Word文件需要借助第三方库Apache POI和Docx4J。下面是一个基本的步骤: 1. 创建一个空的Word文件(.docx)作为模板。 2. 使用Microsoft Word或其他Word处理软件,在模板中插入书签或自定义XML标记。这些标记将标识出文档中需要插入数据的位置。 3. 在Java中,使用Apache POI或Docx4J加载Word模板。 4. 查找并获取文档中的书签或自定义XML标记。 5. 使用Java代码生成需要插入的数据。 6. 使用Apache POI或Docx4J将数据插入到相应的书签或XML标记位置。 7. 保存Word文档。 下面是使用Docx4J生成Word文件的示例代码: ```java //加载模板 WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage.load(new File("template.docx")); //查找书签 MainDocumentPart mainDocumentPart = wordMLPackage.getMainDocumentPart(); HashMap<String,BookmarkStart> bookmarks = new HashMap<String,BookmarkStart>(); List<Object> paragraphs = mainDocumentPart.getContent(); for (Object paragraph : paragraphs) { List<Object> runs = ((P)paragraph).getContent(); for (Object run : runs) { if (run instanceof R) { List<Object> texts = ((R)run).getContent(); for (Object text : texts) { if (text instanceof JAXBElement) { JAXBElement<?> element = (JAXBElement<?>)text; if (element.getName().getLocalPart().equals("bookmarkStart")) { BookmarkStart bookmarkStart = (BookmarkStart)element.getValue(); bookmarks.put(bookmarkStart.getName(), bookmarkStart); } } } } } } //插入数据 BookmarkStart bookmarkStart = bookmarks.get("bookmarkName"); Text text = Context.getWmlObjectFactory().createText(); text.setValue("Hello World!"); R run = Context.getWmlObjectFactory().createR(); run.getContent().add(text); mainDocumentPart.getContent().add(run); //保存文件 wordMLPackage.save(new File("output.docx")); ``` 此代码中,我们首先加载模板,然后使用Docx4J查找书签。我们可以使用书签的名称来查找它们,将其存储在HashMap中以便后面插入数据时使用。最后,我们插入一个简单的文本字符串并保存文件

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

胡#

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值