gnotes随笔记导出文件保存到数据库

GNotes随笔记导出文件保存到数据库

gnotes随笔记导出文件转换并保存到数据库/json文件



前言

因为随笔记官方现在免费版不能自动同步笔记信息了,只有vip才有同步的功能,而且图片也只支持压缩图了。但是我在随笔记上已经记录了很多笔记,迁移也不方便,而且笔记没有保存到自己手上始终不太放心,于是就想着能不能将随笔记我的笔记信息都保存下来。


一、如何获取gnote随笔记的笔记信息

gnote官方提供了导入/导出功能,安卓版的gnote随笔记app内的设置->导入/导出->就能立即导出到手机sd卡(或内置储存)上。

但是导出的文件都是html格式的,当网页打开查看倒是没有问题,但是要统一整理就不是很方便了。

随笔记导出的html文件都是靠a标签跳转的,仅仅支持查看,要想搜索或者其他操作就不支持了。

二、整体流程

1.流程非常简单,因为考虑到后期使用springBoot来管理笔记内容,所以使用的是java的解析方式

设置->导出
导入项目中
Jsoup
python
创建/修改时间 标题 内容等..
转base64编码
oos客户端上传
返回的url
安装gnote随笔记App登录同步
获取导出文件包
遍历读取导出文件夹
获取笔记html文件
解析html文件提取参数
文本信息
图片信息
保存到数据库blob
保存到数据库或是json文件
保存到oos对象存储

二、读取解析导出的html文件

今天这篇主要是将的如何读取解析随笔记导出的html文件

1.首先打开一个html文件

导出文件在这里插入图片描述

导出示例文件(html示例):

<!-- 前面省略 -->
<input name="X-Folder-Name" type="hidden" value="分类"/>
<input name="X-Reminder-Date" type="hidden" value="0"/>
<input name="X-Starred" type="hidden" value="false"/>
<div class="wrap">
    <div id="header">
    	<a href="../notes.html" onClick="window.GoBack.goBack(); ">返回
    		<strong></strong><span></span>
   		</a>
   		<h4 class="text_overflow">标题</h4>
  	</div>
    <div class="content">
    	<p class="create_time">创建时间: 2013/2/12 18:06<br>修改时间: 2013/2/13 16:14</p>
        <p class="note_content">笔记内容 <br>第二行笔记内容... </p>
        <div style="width: 100%; max-width: 800px; margin: 0 auto; text-align:center;">
        	<a href="a9a4ad48602d46978fea24e36362975c_IMG_20130119_115809.jpg">
        		<img class="image" src="a9a4ad48602d46978fea24e36362975c_IMG_20130119_115809.jpg" style="max-width: 800px;" width="85%"/>
             </a>
        </div>
     </div>
</div>
</body>
</html>

导出的html基本上就是这样一个模板

建立笔记模型

笔记html文件里的信息大概就是这几个:标题、类型、创建/修改时间、笔记内容、图片
所以据此可以建立笔记实体类和表结构:

@Data
@ToString
public class Gnote {
    private String id;        // id
    private String title;        // 标题
    private String type;        // 类型
    private Date createTime;        // 创建时间
    private Date updateTime;        // 修改时间
    private String noteContent;        // 笔记内容
    private String html;        // 笔记内容原始html(可不要)
    private String imageBase64List;        // 图片base64编码列表或是返回的oos地址
    private String userId;        // 关联用户名
}

因为是根据表结构生成的实体类,注释不太规范

CREATE TABLE `g_note`  (
  `id` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '主键id',
  `title` varchar(512) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '标题',
  `type` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '类型',
  `create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
  `update_time` datetime(0) NULL DEFAULT NULL COMMENT '修改时间',
  `note_content` varchar(4000) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '笔记内容',
  `html` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '笔记内容原始html',
  `image_base64_list` blob NULL DEFAULT NULL COMMENT '图片base64编码列表或是返回的oos地址',
  `user_id` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '关联用户名',
  PRIMARY KEY (`id`) USING BTREE,
  INDEX `笔记索引`(`title`, `type`, `update_time`, `user_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = 'gnote笔记信息' ROW_FORMAT = Dynamic;

解析html文件

因为导出的html的标签元素都是相同的,所以可以通过解析html来获取对应的信息还是扫描文件夹获取所有html文件

扫描文件夹
    /**
     * 扫描文件夹
     *
     * @param dir
     */
    private List<File> scanDir(String dir, Boolean getDir) {
        File dirFile = new File(dir);
        List<File> jsonArray = new ArrayList<File>();
        if (dirFile.exists() && dirFile.isDirectory()) {
            File[] tempList = dirFile.listFiles();
            for (int i = 0; i < tempList.length; i++) {
                if (tempList[i].isFile() && tempList[i].getName().contains(".")) {
                    String lastName = tempList[i].getName().substring(tempList[i].getName().lastIndexOf("."), tempList[i].getName().length());
                    if (!getDir && ".html".equals(lastName.toLowerCase())) {
                        jsonArray.add(tempList[i]);
                    }
                }
                if (tempList[i].isDirectory()) {
                    //这里就不递归了,
                    if (getDir) {
                        jsonArray.add(tempList[i]);
                    }
                }
            }
        }
//        logger.info(jsonArray.toString());
        return jsonArray;
    }

读取html文件内容
    /**
     * 读取文本文件
     *
     * @param file 文件
     * @return
     */
    public static String readTextFile(File file) {
        return readTextFile(file, "UTF-8");
    }
    /**
     * 读取文本文件
     *
     * @param file        文件
     * @param charsetName 编码
     * @return
     */
    public static String readTextFile(File file, String charsetName) {
        //找到文件,读取文件
        BufferedReader bufferedReader1 = null;
        StringBuffer content = new StringBuffer();
        try {
            bufferedReader1 = new BufferedReader(new InputStreamReader(new FileInputStream(file), charsetName));
            String line = null;
            while ((line = bufferedReader1.readLine()) != null) {
                content.append(line).append(System.getProperties().getProperty("lineSeparator"));
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (bufferedReader1 != null) {
                try {
                    bufferedReader1.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return content.toString();
    }

解析html文件内容

 /**
     * Jsoup解析html
     *
     * @param file
     * @return
     */
    private Gnote getNotes(File file) {
        Gnote gnote = new Gnote();
        String text = readTextFile(file);
        try {
        	//解析为Document对象
            Document document = Jsoup.parse(text);
            if (document != null) {
            	//获取标题
                Elements el1 = document.select("#header .text_overflow");
                String title = el1.first().text();
                gnote.setTitle(title);
                String type = this.getFirstElement(document, "input[name='X-Folder-Name']").val();
                if (null == type || "".equals(type)) {
                    type = "allType";
                }
                gnote.setType(type);
                //获取并解析时间信息
                String time = this.getFirstElement(document, ".content .create_time").text();
                String text1 = "创建时间: ";
                String text2 = " 修改时间: ";
                String createTime = time.substring(text1.length(), time.indexOf(text2));
                String updateTime = time.substring(time.indexOf(text2) + text2.length(), time.length());
                gnote.setCreateTime(sdf1.parse(createTime));
                gnote.setUpdateTime(sdf1.parse(updateTime));
                //获取笔记内容信息
                Element contentElement = this.getFirstElement(document, ".content .note_content");
                gnote.setHtml(contentElement.html());
                gnote.setNoteContent(contentElement.text());
                gnote.setUserId(userId);
                //获取图片
                JSONArray imagesJsonArray = new JSONArray();
                Elements images = document.select(".content div img");
                if (null != images && images.size() > 0) {
                    for (Element image : images) {
                        String src = image.attr("src");
                        File imageFile = new File(file.getParent() + "/" + src);
                        if (imageFile.exists()) {
                            //图片存在了
                            String imageInfo = this.getImageInfo(imageFile);
                            imagesJsonArray.add(imageInfo);
                        }
                    }
                }
                if (imagesJsonArray.size() > 0) {
                    gnote.setImageBase64List(imagesJsonArray.toString());
                }
                gnote.setId(UUID.fastUUID().toString(true));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return gnote;
    }
    
    /**
     * 获取第一个元素
     *
     * @param document
     * @param cssQuery
     * @return
     */
    private Element getFirstElement(Document document, String cssQuery) {
        return document.select(cssQuery).first();
    }

解析规则都是根据示例html进行解析的,Jsoup的解析规则和jQuery一毛一样,只要你会一点点前端和jQuery,那么解析就没什么问题。解析步骤基本上都在注释中写了


保存Gnote文件

如果是要保存到数据库,那么直接写sql去insert就行了:

    /**
     * 写入数据库
     *
     * @param gnoteList
     * @return
     */
    private int writeToDataBase(List<Gnote> gnoteList) {
        int index = 0;
        //先清除原来的信息 不需要的可以屏蔽
        staticDateBaseConnUtil.edit("delete from g_note", true);
        //遍历插入
        for (Gnote gnote : gnoteList) {
        	//方法的update不会自动提交,遍历多了会抛异常
            index += staticDateBaseConnUtil.edit("insert into g_note(id, title, type, create_time, update_time, note_content, html, image_base64_list, user_id)" +
                            "values(?,?,?,?,?,?,?,?,?)", true,
                    gnote.getId(), gnote.getTitle(), gnote.getType(), gnote.getCreateTime(), gnote.getUpdateTime(), gnote.getNoteContent(), gnote.getHtml(), gnote.getImageBase64List(), gnote.getUserId());
        }
        return index;
    }

如果是要保存到json,那么直接转json并写入到文件就行了:

    /**
     * 写入文件
     *
     * @param gnoteList
     * @return
     */
    private int writeToJsonFile(List<Gnote> gnoteList) {
        JSONArray jsonArray = JSONArray.fromObject(gnoteList);
        String json = jsonArray.toString();
        writeTextFile(new File("保存的地址"), json);
        return gnoteList.size();
    }
    
 	/**
     * 写入文本文件
     *
     * @param file 文件
     * @param text 内容
     */
    public static void writeTextFile(File file, String text) {
        writeTextFile(file, text, "UTF-8");
    }

    /**
     * 写入文本文件
     *
     * @param file        文件
     * @param text        内容
     * @param charsetName 编码
     */
    public static void writeTextFile(File file, String text, String charsetName) {
        //写入文件
        file.delete();
        BufferedWriter writer = null;
        try {
            file.createNewFile();
            writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), charsetName));
            writer.write(text);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (writer != null) {
                try {
                    writer.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

还有图片转base64或者oos上传的就不详述了,有需要后面再补充吧


总结

基本上完整的解析流程大概就是这样了,如果还有不清楚的或是想直接用的;可以直接先拉下面的完整示例项目来看看。

后记

后面要是有空讲一下用springBoot+uniApp来自己管理导出的笔记文件吧,当然功能还很简陋;其实这主要是给gnote笔记备个份和以后转到其他笔记做准备的。


完整的示例项目源码地址: https://gitee.com/anlinxi/gnoteReaderDemo

后记:因为有些不懂代码的童鞋也想使用该功能,所以我做了个页面解析gnote导出的zip文件供大家使用: gnote导出文件解析excel下载
若是笔记里有敏感或重要信息的,还是使用上面的源码自己本地解析吧。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值