mirror of
https://github.com/siyuan-note/siyuan.git
synced 2025-09-22 08:30:42 +02:00
🎨 导入 Markdown 时将 Base64 编码的图片转换为文件 Fix https://github.com/siyuan-note/siyuan/issues/6671
This commit is contained in:
parent
5f5d70740e
commit
31be236557
2 changed files with 108 additions and 7 deletions
|
@ -30,6 +30,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/88250/gulu"
|
||||
"github.com/88250/lute"
|
||||
"github.com/88250/lute/ast"
|
||||
"github.com/88250/lute/parse"
|
||||
"github.com/dustin/go-humanize"
|
||||
|
@ -439,10 +440,30 @@ func moveTree(tree *parse.Tree) {
|
|||
}
|
||||
}
|
||||
|
||||
func parseStdMd(markdown []byte) (ret *parse.Tree) {
|
||||
luteEngine := lute.New()
|
||||
luteEngine.SetFootnotes(false)
|
||||
luteEngine.SetToC(false)
|
||||
luteEngine.SetIndentCodeBlock(false)
|
||||
luteEngine.SetAutoSpace(false)
|
||||
luteEngine.SetHeadingID(false)
|
||||
luteEngine.SetSetext(false)
|
||||
luteEngine.SetYamlFrontMatter(false)
|
||||
luteEngine.SetLinkRef(false)
|
||||
ret = parse.Parse("", markdown, luteEngine.ParseOptions)
|
||||
genTreeID(ret)
|
||||
return
|
||||
}
|
||||
|
||||
func parseKTree(kramdown []byte) (ret *parse.Tree) {
|
||||
luteEngine := NewLute()
|
||||
ret = parse.Parse("", kramdown, luteEngine.ParseOptions)
|
||||
ast.Walk(ret.Root, func(n *ast.Node, entering bool) ast.WalkStatus {
|
||||
genTreeID(ret)
|
||||
return
|
||||
}
|
||||
|
||||
func genTreeID(tree *parse.Tree) {
|
||||
ast.Walk(tree.Root, func(n *ast.Node, entering bool) ast.WalkStatus {
|
||||
if !entering {
|
||||
return ast.WalkContinue
|
||||
}
|
||||
|
@ -475,7 +496,7 @@ func parseKTree(kramdown []byte) (ret *parse.Tree) {
|
|||
}
|
||||
return ast.WalkContinue
|
||||
})
|
||||
ret.Root.KramdownIAL = parse.Tokens2IAL(ret.Root.LastChild.Tokens)
|
||||
tree.Root.KramdownIAL = parse.Tokens2IAL(tree.Root.LastChild.Tokens)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -18,9 +18,13 @@ package model
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"image"
|
||||
"image/jpeg"
|
||||
"image/png"
|
||||
"io"
|
||||
"io/fs"
|
||||
"math/rand"
|
||||
|
@ -403,6 +407,9 @@ func ImportFromLocalPath(boxID, localPath string, toPath string) (err error) {
|
|||
}
|
||||
boxLocalPath = filepath.Join(util.DataDir, boxID)
|
||||
|
||||
base64TmpDir := filepath.Join(util.TempDir, "base64")
|
||||
os.MkdirAll(base64TmpDir, 0755)
|
||||
|
||||
luteEngine := NewLute()
|
||||
if gulu.File.IsDir(localPath) {
|
||||
// 收集所有资源文件
|
||||
|
@ -419,8 +426,7 @@ func ImportFromLocalPath(boxID, localPath string, toPath string) (err error) {
|
|||
}
|
||||
|
||||
if !strings.HasSuffix(info.Name(), ".md") && !strings.HasSuffix(info.Name(), ".markdown") {
|
||||
dest := currentPath
|
||||
assets[dest] = currentPath
|
||||
assets[currentPath] = currentPath
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
|
@ -480,7 +486,7 @@ func ImportFromLocalPath(boxID, localPath string, toPath string) (err error) {
|
|||
return io.EOF
|
||||
}
|
||||
|
||||
tree = parseKTree(data)
|
||||
tree = parseStdMd(data)
|
||||
if nil == tree {
|
||||
logging.LogErrorf("parse tree [%s] failed", currentPath)
|
||||
return nil
|
||||
|
@ -507,6 +513,11 @@ func ImportFromLocalPath(boxID, localPath string, toPath string) (err error) {
|
|||
}
|
||||
|
||||
dest := n.TokensStr()
|
||||
if strings.HasPrefix(dest, "data:image") && strings.Contains(dest, ";base64,") {
|
||||
processBase64Img(n, dest, base64TmpDir, assetDirPath, err)
|
||||
return ast.WalkContinue
|
||||
}
|
||||
|
||||
if !util.IsRelativePath(dest) || "" == dest {
|
||||
return ast.WalkContinue
|
||||
}
|
||||
|
@ -570,7 +581,7 @@ func ImportFromLocalPath(boxID, localPath string, toPath string) (err error) {
|
|||
if nil != err {
|
||||
return err
|
||||
}
|
||||
tree := parseKTree(data)
|
||||
tree := parseStdMd(data)
|
||||
if nil == tree {
|
||||
msg := fmt.Sprintf("parse tree [%s] failed", localPath)
|
||||
logging.LogErrorf(msg)
|
||||
|
@ -595,6 +606,11 @@ func ImportFromLocalPath(boxID, localPath string, toPath string) (err error) {
|
|||
}
|
||||
|
||||
dest := n.TokensStr()
|
||||
if strings.HasPrefix(dest, "data:image") && strings.Contains(dest, ";base64,") {
|
||||
processBase64Img(n, dest, base64TmpDir, assetDirPath, err)
|
||||
return ast.WalkContinue
|
||||
}
|
||||
|
||||
if !util.IsRelativePath(dest) {
|
||||
return ast.WalkContinue
|
||||
}
|
||||
|
@ -641,6 +657,71 @@ func ImportFromLocalPath(boxID, localPath string, toPath string) (err error) {
|
|||
return
|
||||
}
|
||||
|
||||
func processBase64Img(n *ast.Node, dest string, base64TmpDir string, assetDirPath string, err error) {
|
||||
sep := strings.Index(dest, ";base64,")
|
||||
var decodeErr error
|
||||
unbased, decodeErr := base64.StdEncoding.DecodeString(dest[sep+8:])
|
||||
if nil != decodeErr {
|
||||
logging.LogErrorf("decode base64 image failed: %s", decodeErr)
|
||||
return
|
||||
}
|
||||
dataReader := bytes.NewReader(unbased)
|
||||
var img image.Image
|
||||
var ext string
|
||||
typ := dest[5:sep]
|
||||
switch typ {
|
||||
case "image/png":
|
||||
img, decodeErr = png.Decode(dataReader)
|
||||
ext = ".png"
|
||||
case "image/jpeg":
|
||||
img, decodeErr = jpeg.Decode(dataReader)
|
||||
ext = ".jpg"
|
||||
default:
|
||||
logging.LogWarnf("unsupported base64 image type [%s]", typ)
|
||||
return
|
||||
}
|
||||
if nil != decodeErr {
|
||||
logging.LogErrorf("decode base64 image failed: %s", decodeErr)
|
||||
return
|
||||
}
|
||||
|
||||
name := "image" + ext
|
||||
alt := n.Parent.ChildByType(ast.NodeLinkText)
|
||||
if nil != alt {
|
||||
name = alt.TokensStr() + ext
|
||||
}
|
||||
name = util.FilterFileName(name)
|
||||
name = util.AssetName(name)
|
||||
|
||||
tmp := filepath.Join(base64TmpDir, name)
|
||||
tmpFile, openErr := os.OpenFile(tmp, os.O_RDWR|os.O_CREATE, 0644)
|
||||
if nil != openErr {
|
||||
logging.LogErrorf("open temp file [%s] failed: %s", tmp, openErr)
|
||||
return
|
||||
}
|
||||
|
||||
var encodeErr error
|
||||
switch typ {
|
||||
case "image/png":
|
||||
encodeErr = png.Encode(tmpFile, img)
|
||||
case "image/jpeg":
|
||||
encodeErr = jpeg.Encode(tmpFile, img, &jpeg.Options{Quality: 100})
|
||||
}
|
||||
if nil != encodeErr {
|
||||
logging.LogErrorf("encode base64 image failed: %s", encodeErr)
|
||||
tmpFile.Close()
|
||||
return
|
||||
}
|
||||
tmpFile.Close()
|
||||
|
||||
assetTargetPath := filepath.Join(assetDirPath, name)
|
||||
if err = filelock.Copy(tmp, assetTargetPath); nil != err {
|
||||
logging.LogErrorf("copy asset from [%s] to [%s] failed: %s", tmp, assetTargetPath, err)
|
||||
return
|
||||
}
|
||||
n.Tokens = []byte("assets/" + name)
|
||||
}
|
||||
|
||||
func imgHtmlBlock2InlineImg(tree *parse.Tree) {
|
||||
imgHtmlBlocks := map[*ast.Node]*html.Node{}
|
||||
ast.Walk(tree.Root, func(n *ast.Node, entering bool) ast.WalkStatus {
|
||||
|
@ -691,7 +772,6 @@ func imgHtmlBlock2InlineImg(tree *parse.Tree) {
|
|||
n.InsertBefore(p)
|
||||
n.Unlink()
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue