Private
Public Access
1
0

修复: 网站预览资源路径+七牛目录层级

This commit is contained in:
2026-05-13 21:16:33 +08:00
parent 2a363fd729
commit 316e517989
6 changed files with 63 additions and 93 deletions

View File

@@ -155,7 +155,6 @@ const _restoreLastPlayed = () => {
if (!track) return
lastPlayedPath.value = saved
playingPath.value = saved
emit('switch', track)
}
restorePlaylist().then(_restoreLastPlayed)

View File

@@ -898,6 +898,9 @@ var sa=Element.prototype.setAttribute;
Element.prototype.setAttribute=function(n,v){if((n==='src'||n==='href'||n==='data'||n==='poster')&&typeof v==='string')v=rw(v);return sa.call(this,n,v);};
try{var d=Object.getOwnPropertyDescriptor(HTMLScriptElement.prototype,'src');Object.defineProperty(HTMLScriptElement.prototype,'src',{set:function(v){d.set.call(this,rw(v))},get:d.get,configurable:true});}catch(e){}
try{var d2=Object.getOwnPropertyDescriptor(HTMLLinkElement.prototype,'href');Object.defineProperty(HTMLLinkElement.prototype,'href',{set:function(v){d2.set.call(this,rw(v))},get:d2.get,configurable:true});}catch(e){}
try{var d3=Object.getOwnPropertyDescriptor(HTMLImageElement.prototype,'src');Object.defineProperty(HTMLImageElement.prototype,'src',{set:function(v){d3.set.call(this,rw(v))},get:d3.get,configurable:true});}catch(e){}
try{var d4=Object.getOwnPropertyDescriptor(HTMLSourceElement.prototype,'src');Object.defineProperty(HTMLSourceElement.prototype,'src',{set:function(v){d4.set.call(this,rw(v))},get:d4.get,configurable:true});}catch(e){}
try{var d5=Object.getOwnPropertyDescriptor(HTMLIFrameElement.prototype,'src');Object.defineProperty(HTMLIFrameElement.prototype,'src',{set:function(v){d5.set.call(this,rw(v))},get:d5.get,configurable:true});}catch(e){}
})();
</script>`

View File

@@ -0,0 +1,47 @@
package filesystem
import (
"regexp"
"strings"
)
var (
htmlResRegex = regexp.MustCompile(`(?:src|href|data|poster)=["']([^"']+)["']`)
htmlCssUrlRe = regexp.MustCompile(`url\(\s*["']?([^"')]+)["']?\s*\)`)
)
// ExtractHtmlResources 从 HTML 内容提取资源路径
func ExtractHtmlResources(html string) []string {
seen := make(map[string]bool)
var resources []string
add := func(v string) {
v = strings.TrimSpace(v)
if v != "" && !seen[v] {
seen[v] = true
resources = append(resources, v)
}
}
for _, m := range htmlResRegex.FindAllStringSubmatch(html, -1) {
if len(m) > 1 {
add(m[1])
}
}
for _, m := range htmlCssUrlRe.FindAllStringSubmatch(html, -1) {
if len(m) > 1 {
add(m[1])
}
}
return resources
}
// ShouldSkipResource 判断资源路径是否应跳过
func ShouldSkipResource(p string) bool {
return strings.HasPrefix(p, "data:") ||
strings.HasPrefix(p, "http://") ||
strings.HasPrefix(p, "https://") ||
strings.HasPrefix(p, "//") ||
strings.HasPrefix(p, "#") ||
strings.HasPrefix(p, "javascript:") ||
strings.HasPrefix(p, "mailto:") ||
strings.HasPrefix(p, "blob:")
}

View File

@@ -480,6 +480,9 @@ func (c *Client) ListFiles(ctx context.Context, options *oss.ListOptions) (*oss.
if options.Marker != "" {
path += "&marker=" + options.Marker
}
if options.Delimiter != "" {
path += "&delimiter=" + options.Delimiter
}
// 使用 GET 方法和 RSF API
resp, err := c.doRSFRequest("GET", path)

View File

@@ -8,7 +8,6 @@ import (
"os"
"path"
"path/filepath"
"regexp"
"strings"
"sync"
"time"
@@ -640,9 +639,7 @@ func (s *Service) RenamePath(connID string, oldPath string, newPath string) (*fi
return result, nil
}
// htmlResourceRegex 提取 HTML 资源引用的正则
var htmlResourceRegex = regexp.MustCompile(`(?:src|href|data|poster)=["']([^"']+)["']`)
var htmlCssUrlRegex = regexp.MustCompile(`url\(\s*["']?([^"')]+)["']?\s*\)`)
// DownloadSiteForPreview 下载 HTML 及其引用的资源到临时目录
// 对绝对路径(/开头)从 HTML 目录逐级向上嗅探网站根目录
@@ -695,7 +692,7 @@ func (s *Service) DownloadSiteForPreview(connID string, rawPath string) (string,
if err != nil {
return htmlLocalPath, nil // HTML 已下载,资源解析失败不影响
}
resources := extractHtmlResources(string(htmlContent))
resources := filesystem.ExtractHtmlResources(string(htmlContent))
// 4. 下载资源
htmlOssDir := keyDir
@@ -716,7 +713,7 @@ func (s *Service) DownloadSiteForPreview(connID string, rawPath string) (string,
}
for _, resPath := range resources {
if shouldSkipResource(resPath) {
if filesystem.ShouldSkipResource(resPath) {
continue
}
@@ -823,51 +820,13 @@ func supplementDir(c oss.OSSProvider, ctx context.Context, remoteDir string, tmp
if strings.HasSuffix(f.Key, "/") || f.Size == 0 {
continue
}
relPath := strings.TrimPrefix(f.Key, siteRoot)
localPath := filepath.Join(tmpDir, filepath.FromSlash(relPath))
localPath := filepath.Join(tmpDir, filepath.FromSlash(f.Key))
if _, err := os.Stat(localPath); err == nil {
continue
}
downloadResource(c, ctx, f.Key, localPath)
}
}
func extractHtmlResources(html string) []string {
seen := make(map[string]bool)
var resources []string
add := func(v string) {
v = strings.TrimSpace(v)
if v != "" && !seen[v] {
seen[v] = true
resources = append(resources, v)
}
}
for _, m := range htmlResourceRegex.FindAllStringSubmatch(html, -1) {
if len(m) > 1 {
add(m[1])
}
}
for _, m := range htmlCssUrlRegex.FindAllStringSubmatch(html, -1) {
if len(m) > 1 {
add(m[1])
}
}
return resources
}
// shouldSkipResource 判断资源路径是否应跳过
func shouldSkipResource(p string) bool {
return strings.HasPrefix(p, "data:") ||
strings.HasPrefix(p, "http://") ||
strings.HasPrefix(p, "https://") ||
strings.HasPrefix(p, "//") ||
strings.HasPrefix(p, "#") ||
strings.HasPrefix(p, "javascript:") ||
strings.HasPrefix(p, "mailto:") ||
strings.HasPrefix(p, "blob:")
}
// DownloadToTemp 下载文件到本地临时目录(带 SQLite 缓存)
func (s *Service) DownloadToTemp(connID string, rawPath string) (string, error) {

View File

@@ -8,7 +8,6 @@ import (
"os"
"path"
"path/filepath"
"regexp"
"strconv"
"strings"
"time"
@@ -19,10 +18,7 @@ import (
sftpclient "github.com/pkg/sftp"
)
var (
sftpResRegex = regexp.MustCompile(`(?:src|href|data|poster)=["']([^"']+)["']`)
sftpCssUrlRe = regexp.MustCompile(`url\(\s*["']?([^"')]+)["']?\s*\)`)
)
// Service SFTP 文件操作服务
type Service struct {
@@ -383,7 +379,7 @@ func (s *Service) DownloadSiteForPreview(connID string, remotePath string) (stri
if err != nil {
return htmlLocalPath, nil
}
resources := sftpExtractResources(string(htmlContent))
resources := filesystem.ExtractHtmlResources(string(htmlContent))
// 5. 下载静态引用资源(嗅探网站根)
htmlRemoteDir := keyDir
@@ -404,7 +400,7 @@ func (s *Service) DownloadSiteForPreview(connID string, remotePath string) (stri
}
for _, resPath := range resources {
if sftpShouldSkip(resPath) {
if filesystem.ShouldSkipResource(resPath) {
continue
}
isAbsolute := strings.HasPrefix(resPath, "/")
@@ -513,9 +509,7 @@ func sftpSupplementDir(s *Service, c *Client, remoteDir string, tmpDir string, s
continue
}
fullPath := path.Join(remoteDir, entry.Name())
relPath := strings.TrimPrefix(fullPath, siteRoot)
relPath = strings.TrimPrefix(relPath, "/")
localPath := filepath.Join(tmpDir, filepath.FromSlash(relPath))
localPath := filepath.Join(tmpDir, filepath.FromSlash(fullPath))
if _, err := os.Stat(localPath); err == nil {
continue
}
@@ -707,38 +701,3 @@ func toFileOperationResult(m map[string]interface{}, isDir bool) *filesystem.Fil
}
}
// sftpExtractResources 从 HTML 内容提取资源路径
func sftpExtractResources(html string) []string {
seen := make(map[string]bool)
var resources []string
add := func(v string) {
v = strings.TrimSpace(v)
if v != "" && !seen[v] {
seen[v] = true
resources = append(resources, v)
}
}
for _, m := range sftpResRegex.FindAllStringSubmatch(html, -1) {
if len(m) > 1 {
add(m[1])
}
}
for _, m := range sftpCssUrlRe.FindAllStringSubmatch(html, -1) {
if len(m) > 1 {
add(m[1])
}
}
return resources
}
// sftpShouldSkip 判断资源路径是否应跳过
func sftpShouldSkip(p string) bool {
return strings.HasPrefix(p, "data:") ||
strings.HasPrefix(p, "http://") ||
strings.HasPrefix(p, "https://") ||
strings.HasPrefix(p, "//") ||
strings.HasPrefix(p, "#") ||
strings.HasPrefix(p, "javascript:") ||
strings.HasPrefix(p, "mailto:") ||
strings.HasPrefix(p, "blob:")
}