Compare commits
No commits in common. "main" and "v1.2.1" have entirely different histories.
1
.gitignore
vendored
@ -31,4 +31,3 @@ User
|
|||||||
config.json
|
config.json
|
||||||
wechatDataBackup.exe
|
wechatDataBackup.exe
|
||||||
app.log
|
app.log
|
||||||
app-*.log
|
|
39
README.md
@ -1,34 +1,13 @@
|
|||||||
<p align="center" style="text-align: center">
|
|
||||||
<img src="./res/logo_256.png" width="15%"><br/>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p align="center">
|
|
||||||
<b>wechatDataBackup: PC微信聊天记录数据导出工具</b>
|
|
||||||
<br/>
|
|
||||||
<br/>
|
|
||||||
<a href="https://github.com/git-jiadong/wechatDataBackup/stargazers">
|
|
||||||
<img src="https://img.shields.io/github/stars/git-jiadong/wechatDataBackup" alt="GitHub Star"/>
|
|
||||||
</a>
|
|
||||||
<a href="https://github.com/git-jiadong/wechatDataBackup/releases">
|
|
||||||
<img src="https://img.shields.io/github/downloads/git-jiadong/wechatDataBackup/total" alt="downloads" />
|
|
||||||
</a>
|
|
||||||
<a href="https://github.com/git-jiadong/wechatDataBackup/releases">
|
|
||||||
<img src="https://img.shields.io/github/v/release/git-jiadong/wechatDataBackup" alt="releases version"/>
|
|
||||||
</a>
|
|
||||||
<a href="https://github.com/git-jiadong/wechatDataBackup/commits/main">
|
|
||||||
<img src="https://img.shields.io/github/last-commit/git-jiadong/wechatDataBackup" alt="last commit" />
|
|
||||||
</a>
|
|
||||||
<a href="https://github.com/git-jiadong/wechatDataBackup" >
|
|
||||||
<img src="https://img.shields.io/github/languages/top/git-jiadong/wechatDataBackup" alt="languages"/>
|
|
||||||
</a>
|
|
||||||
<a href="https://github.com/git-jiadong/wechatDataBackup" >
|
|
||||||
<img src="https://img.shields.io/github/repo-size/git-jiadong/wechatDataBackup" alt="repo size" />
|
|
||||||
</a>
|
|
||||||
<a href="https://github.com/git-jiadong/wechatDataBackup/blob/main/LICENSE">
|
|
||||||
<img src="https://img.shields.io/github/license/git-jiadong/wechatDataBackup" alt="license" />
|
|
||||||
</a>
|
|
||||||
</p>
|
|
||||||
# wechatDataBackup
|
# wechatDataBackup
|
||||||
|
PC微信聊天记录数据导出工具
|
||||||
|
|
||||||
|
[](https://github.com/git-jiadong/wechatDataBackup/stargazers)
|
||||||
|
[](https://github.com/git-jiadong/wechatDataBackup/releases)
|
||||||
|
[](https://github.com/git-jiadong/wechatDataBackup/releases)
|
||||||
|
[](https://github.com/git-jiadong/wechatDataBackup/commits/main)
|
||||||
|
[](https://github.com/git-jiadong/wechatDataBackup)
|
||||||
|
[](https://github.com/git-jiadong/wechatDataBackup)
|
||||||
|
[](https://github.com/git-jiadong/wechatDataBackup/blob/main/LICENSE)
|
||||||
|
|
||||||
* 基于wails开发 + React前端,实现PC端微信聊天记录一键导出功能。
|
* 基于wails开发 + React前端,实现PC端微信聊天记录一键导出功能。
|
||||||
* 导出后数据可以做永久化保存,即使微信停止支持,聊天记录也可以随时查看。
|
* 导出后数据可以做永久化保存,即使微信停止支持,聊天记录也可以随时查看。
|
||||||
|
25
app.go
@ -23,7 +23,7 @@ const (
|
|||||||
configDefaultUserKey = "userConfig.defaultUser"
|
configDefaultUserKey = "userConfig.defaultUser"
|
||||||
configUsersKey = "userConfig.users"
|
configUsersKey = "userConfig.users"
|
||||||
configExportPathKey = "exportPath"
|
configExportPathKey = "exportPath"
|
||||||
appVersion = "v1.2.4"
|
appVersion = "v1.2.1"
|
||||||
)
|
)
|
||||||
|
|
||||||
type FileLoader struct {
|
type FileLoader struct {
|
||||||
@ -832,6 +832,14 @@ func (a *App) ExportWeChatDataByUserName(userName, path string) string {
|
|||||||
return "WeChatExportDataByUserName failed:" + err.Error()
|
return "WeChatExportDataByUserName failed:" + err.Error()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
exeSrcPath := a.FLoader.FilePrefix + "\\" + "wechatDataBackup.exe"
|
||||||
|
exeDstPath := exPath + "\\" + "wechatDataBackup.exe"
|
||||||
|
_, err = utils.CopyFile(exeSrcPath, exeDstPath)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("CopyFile:", err)
|
||||||
|
return "CopyFile:" + err.Error()
|
||||||
|
}
|
||||||
|
|
||||||
config := map[string]interface{}{
|
config := map[string]interface{}{
|
||||||
"exportpath": ".\\",
|
"exportpath": ".\\",
|
||||||
"userconfig": map[string]interface{}{
|
"userconfig": map[string]interface{}{
|
||||||
@ -853,21 +861,6 @@ func (a *App) ExportWeChatDataByUserName(userName, path string) string {
|
|||||||
return "WriteFile:" + err.Error()
|
return "WriteFile:" + err.Error()
|
||||||
}
|
}
|
||||||
|
|
||||||
exeSrcPath, err := os.Executable()
|
|
||||||
if err != nil {
|
|
||||||
log.Println("Executable:", exeSrcPath)
|
|
||||||
return "Executable:" + err.Error()
|
|
||||||
}
|
|
||||||
|
|
||||||
exeDstPath := exPath + "\\" + "wechatDataBackup.exe"
|
|
||||||
log.Printf("Copy [%s] -> [%s]\n", exeSrcPath, exeDstPath)
|
|
||||||
_, err = utils.CopyFile(exeSrcPath, exeDstPath)
|
|
||||||
if err != nil {
|
|
||||||
log.Println("CopyFile:", err)
|
|
||||||
return "CopyFile:" + err.Error()
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
|
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 7.7 KiB |
Before Width: | Height: | Size: 82 KiB After Width: | Height: | Size: 411 KiB |
13
changelog.md
@ -1,16 +1,3 @@
|
|||||||
## v1.2.4
|
|
||||||
1. 修复FileStorage\Image文件没有导出的问题
|
|
||||||
2. 增加图片定位到聊天位置的功能
|
|
||||||
3. 导出界面增加提示
|
|
||||||
|
|
||||||
## v1.2.3
|
|
||||||
1. 修改程序ICON
|
|
||||||
|
|
||||||
## v1.2.2
|
|
||||||
1. UI统一使用“思源黑体”字体
|
|
||||||
2. 修复导出分享会话时可执行文件可能拷贝错误的情况
|
|
||||||
3. 修复微信存储路径为网盘时,解密会失败的情况
|
|
||||||
|
|
||||||
## v1.2.0
|
## v1.2.0
|
||||||
1. 实现最后浏览位置记录和书签功能
|
1. 实现最后浏览位置记录和书签功能
|
||||||
2. 实现会话导出分享功能
|
2. 实现会话导出分享功能
|
||||||
|
BIN
frontend/dist/assets/110_怄火.896c41d9.png
vendored
Before Width: | Height: | Size: 11 KiB |
BIN
frontend/dist/assets/112_左哼哼.5425438e.png
vendored
Before Width: | Height: | Size: 11 KiB |
BIN
frontend/dist/assets/113_哈欠.fe9c8521.png
vendored
Before Width: | Height: | Size: 13 KiB |
1
frontend/dist/assets/index.00f6955e.css
vendored
1
frontend/dist/assets/index.0393a903.css
vendored
Normal file
533
frontend/dist/assets/index.8be36b27.js
vendored
533
frontend/dist/assets/index.e4a8c0f4.js
vendored
Normal file
BIN
frontend/dist/assets/logo.8df6944e.png
vendored
Before Width: | Height: | Size: 28 KiB |
BIN
frontend/dist/assets/nunito-v16-latin-regular.06f3af3f.woff2
vendored
Normal file
BIN
frontend/dist/assets/red_packet.704bd303.png
vendored
Before Width: | Height: | Size: 91 KiB |
BIN
frontend/dist/assets/思源黑体-Normal.df5ff3ec.otf
vendored
4
frontend/dist/index.html
vendored
@ -4,8 +4,8 @@
|
|||||||
<meta charset="UTF-8"/>
|
<meta charset="UTF-8"/>
|
||||||
<meta content="width=device-width, initial-scale=1.0" name="viewport"/>
|
<meta content="width=device-width, initial-scale=1.0" name="viewport"/>
|
||||||
<title>wechatDataBackup</title>
|
<title>wechatDataBackup</title>
|
||||||
<script type="module" crossorigin src="/assets/index.8be36b27.js"></script>
|
<script type="module" crossorigin src="/assets/index.e4a8c0f4.js"></script>
|
||||||
<link rel="stylesheet" href="/assets/index.00f6955e.css">
|
<link rel="stylesheet" href="/assets/index.0393a903.css">
|
||||||
</head>
|
</head>
|
||||||
<body >
|
<body >
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
|
@ -310,7 +310,6 @@ func exportWeChatVideoAndFile(info WeChatInfo, expPath string, progress chan<- s
|
|||||||
videoRootPath := info.FilePath + "\\FileStorage\\Video"
|
videoRootPath := info.FilePath + "\\FileStorage\\Video"
|
||||||
fileRootPath := info.FilePath + "\\FileStorage\\File"
|
fileRootPath := info.FilePath + "\\FileStorage\\File"
|
||||||
cacheRootPath := info.FilePath + "\\FileStorage\\Cache"
|
cacheRootPath := info.FilePath + "\\FileStorage\\Cache"
|
||||||
|
|
||||||
rootPaths := []string{videoRootPath, fileRootPath, cacheRootPath}
|
rootPaths := []string{videoRootPath, fileRootPath, cacheRootPath}
|
||||||
|
|
||||||
handleNumber := int64(0)
|
handleNumber := int64(0)
|
||||||
@ -327,9 +326,6 @@ func exportWeChatVideoAndFile(info WeChatInfo, expPath string, progress chan<- s
|
|||||||
go func() {
|
go func() {
|
||||||
for _, rootPath := range rootPaths {
|
for _, rootPath := range rootPaths {
|
||||||
log.Println(rootPath)
|
log.Println(rootPath)
|
||||||
if _, err := os.Stat(rootPath); err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
err := filepath.Walk(rootPath, func(path string, finfo os.FileInfo, err error) error {
|
err := filepath.Walk(rootPath, func(path string, finfo os.FileInfo, err error) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("filepath.Walk:%v\n", err)
|
log.Printf("filepath.Walk:%v\n", err)
|
||||||
@ -403,51 +399,43 @@ func exportWeChatVideoAndFile(info WeChatInfo, expPath string, progress chan<- s
|
|||||||
func exportWeChatBat(info WeChatInfo, expPath string, progress chan<- string) {
|
func exportWeChatBat(info WeChatInfo, expPath string, progress chan<- string) {
|
||||||
progress <- "{\"status\":\"processing\", \"result\":\"export WeChat Dat start\", \"progress\": 21}"
|
progress <- "{\"status\":\"processing\", \"result\":\"export WeChat Dat start\", \"progress\": 21}"
|
||||||
datRootPath := info.FilePath + "\\FileStorage\\MsgAttach"
|
datRootPath := info.FilePath + "\\FileStorage\\MsgAttach"
|
||||||
imageRootPath := info.FilePath + "\\FileStorage\\Image"
|
fileInfo, err := os.Stat(datRootPath)
|
||||||
rootPaths := []string{datRootPath, imageRootPath}
|
if err != nil || !fileInfo.IsDir() {
|
||||||
|
progress <- fmt.Sprintf("{\"status\":\"error\", \"result\":\"%s error\"}", datRootPath)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
handleNumber := int64(0)
|
handleNumber := int64(0)
|
||||||
fileNumber := int64(0)
|
fileNumber := getPathFileNumber(datRootPath, ".dat")
|
||||||
for i := range rootPaths {
|
|
||||||
fileNumber += getPathFileNumber(rootPaths[i], ".dat")
|
|
||||||
}
|
|
||||||
log.Println("DatFileNumber ", fileNumber)
|
|
||||||
|
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
var reportWg sync.WaitGroup
|
var reportWg sync.WaitGroup
|
||||||
quitChan := make(chan struct{})
|
quitChan := make(chan struct{})
|
||||||
taskChan := make(chan [2]string, 100)
|
taskChan := make(chan [2]string, 100)
|
||||||
go func() {
|
go func() {
|
||||||
for i := range rootPaths {
|
err = filepath.Walk(datRootPath, func(path string, finfo os.FileInfo, err error) error {
|
||||||
if _, err := os.Stat(rootPaths[i]); err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
err := filepath.Walk(rootPaths[i], func(path string, finfo os.FileInfo, err error) error {
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("filepath.Walk:%v\n", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if !finfo.IsDir() && strings.HasSuffix(path, ".dat") {
|
|
||||||
expFile := expPath + path[len(info.FilePath):]
|
|
||||||
_, err := os.Stat(filepath.Dir(expFile))
|
|
||||||
if err != nil {
|
|
||||||
os.MkdirAll(filepath.Dir(expFile), 0644)
|
|
||||||
}
|
|
||||||
|
|
||||||
task := [2]string{path, expFile}
|
|
||||||
taskChan <- task
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("filepath.Walk:", err)
|
log.Printf("filepath.Walk:%v\n", err)
|
||||||
progress <- fmt.Sprintf("{\"status\":\"error\", \"result\":\"%v\"}", err)
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !finfo.IsDir() && strings.HasSuffix(path, ".dat") {
|
||||||
|
expFile := expPath + path[len(info.FilePath):]
|
||||||
|
_, err := os.Stat(filepath.Dir(expFile))
|
||||||
|
if err != nil {
|
||||||
|
os.MkdirAll(filepath.Dir(expFile), 0644)
|
||||||
|
}
|
||||||
|
|
||||||
|
task := [2]string{path, expFile}
|
||||||
|
taskChan <- task
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Println("filepath.Walk:", err)
|
||||||
|
progress <- fmt.Sprintf("{\"status\":\"error\", \"result\":\"%v\"}", err)
|
||||||
}
|
}
|
||||||
close(taskChan)
|
close(taskChan)
|
||||||
}()
|
}()
|
||||||
@ -457,7 +445,7 @@ func exportWeChatBat(info WeChatInfo, expPath string, progress chan<- string) {
|
|||||||
go func() {
|
go func() {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
for task := range taskChan {
|
for task := range taskChan {
|
||||||
_, err := os.Stat(task[1])
|
_, err = os.Stat(task[1])
|
||||||
if err == nil {
|
if err == nil {
|
||||||
atomic.AddInt64(&handleNumber, 1)
|
atomic.AddInt64(&handleNumber, 1)
|
||||||
continue
|
continue
|
||||||
@ -610,7 +598,7 @@ func GetWeChatInfo() (list *WeChatInfoList) {
|
|||||||
for _, f := range files {
|
for _, f := range files {
|
||||||
if strings.HasSuffix(f.Path, "\\Media.db") {
|
if strings.HasSuffix(f.Path, "\\Media.db") {
|
||||||
// fmt.Printf("opened %s\n", f.Path[4:])
|
// fmt.Printf("opened %s\n", f.Path[4:])
|
||||||
filePath := f.Path
|
filePath := f.Path[4:]
|
||||||
parts := strings.Split(filePath, string(filepath.Separator))
|
parts := strings.Split(filePath, string(filepath.Separator))
|
||||||
if len(parts) < 4 {
|
if len(parts) < 4 {
|
||||||
log.Println("Error filePath " + filePath)
|
log.Println("Error filePath " + filePath)
|
||||||
|
@ -182,7 +182,6 @@ type WeChatMessage struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type WeChatMessageList struct {
|
type WeChatMessageList struct {
|
||||||
MsgType string `json:"MsgType"`
|
|
||||||
KeyWord string `json:"KeyWord"`
|
KeyWord string `json:"KeyWord"`
|
||||||
Total int `json:"Total"`
|
Total int `json:"Total"`
|
||||||
Rows []WeChatMessage `json:"Rows"`
|
Rows []WeChatMessage `json:"Rows"`
|
||||||
@ -693,7 +692,6 @@ func (P *WechatDataProvider) WeChatGetMessageListByKeyWord(userName string, time
|
|||||||
List := &WeChatMessageList{}
|
List := &WeChatMessageList{}
|
||||||
List.Rows = make([]WeChatMessage, 0)
|
List.Rows = make([]WeChatMessage, 0)
|
||||||
List.KeyWord = keyWord
|
List.KeyWord = keyWord
|
||||||
List.MsgType = msgType
|
|
||||||
_time := time
|
_time := time
|
||||||
selectPagesize := pageSize
|
selectPagesize := pageSize
|
||||||
if keyWord != "" || msgType != "" {
|
if keyWord != "" || msgType != "" {
|
||||||
@ -734,7 +732,6 @@ func (P *WechatDataProvider) WeChatGetMessageListByType(userName string, time in
|
|||||||
|
|
||||||
List := &WeChatMessageList{}
|
List := &WeChatMessageList{}
|
||||||
List.Rows = make([]WeChatMessage, 0)
|
List.Rows = make([]WeChatMessage, 0)
|
||||||
List.MsgType = msgType
|
|
||||||
selectTime := time
|
selectTime := time
|
||||||
selectpageSize := 30
|
selectpageSize := 30
|
||||||
needSize := pageSize
|
needSize := pageSize
|
||||||
|
BIN
res/logo.png
Before Width: | Height: | Size: 470 KiB |
BIN
res/logo_128.png
Before Width: | Height: | Size: 8.2 KiB |
BIN
res/logo_256.png
Before Width: | Height: | Size: 28 KiB |