aliyunpan/internal/webdav/webdav_file.go

316 lines
7.9 KiB
Go
Raw Normal View History

2021-12-30 20:24:33 +08:00
package webdav
import (
"context"
2021-12-31 19:55:00 +08:00
"errors"
2021-12-30 20:24:33 +08:00
"github.com/tickstep/aliyunpan-api/aliyunpan"
"github.com/tickstep/library-go/logger"
2021-12-31 00:21:53 +08:00
"io"
2021-12-30 20:24:33 +08:00
"mime"
"os"
"path"
"path/filepath"
2021-12-31 19:55:00 +08:00
"strconv"
2021-12-30 20:24:33 +08:00
"strings"
"time"
"golang.org/x/net/webdav"
)
// NoSniffFileInfo wraps any generic FileInfo interface and bypasses mime type sniffing.
type NoSniffFileInfo struct {
os.FileInfo
}
func (w NoSniffFileInfo) ContentType(ctx context.Context) (contentType string, err error) {
if mimeType := mime.TypeByExtension(path.Ext(w.FileInfo.Name())); mimeType != "" {
// We can figure out the mime from the extension.
return mimeType, nil
} else {
// We can't figure out the mime type without sniffing, call it an octet stream.
return "application/octet-stream", nil
}
}
// 文件系统
type WebDavDir struct {
webdav.Dir
NoSniff bool
panClientProxy *PanClientProxy
fileInfo WebDavFileInfo
}
// slashClean is equivalent to but slightly more efficient than
// path.Clean("/" + name).
func slashClean(name string) string {
if name == "" || name[0] != '/' {
name = "/" + name
}
return path.Clean(name)
}
// formatAbsoluteName 将name名称更改为绝对路径
func (d WebDavDir) formatAbsoluteName(pathStr string) string {
if strings.Index(pathStr, "/") != 0 {
pathStr = d.fileInfo.fullPath + "/" + pathStr
}
return pathStr
}
2021-12-31 19:55:00 +08:00
func (d WebDavDir) getSessionId(ctx context.Context) string {
v := ctx.Value("sessionId")
if v != nil{
return v.(string)
}
return ""
}
2021-12-30 20:24:33 +08:00
func (d WebDavDir) resolve(name string) string {
// This implementation is based on Dir.Open's code in the standard net/http package.
if filepath.Separator != '/' && strings.IndexRune(name, filepath.Separator) >= 0 ||
strings.Contains(name, "\x00") {
return ""
}
dir := string(d.Dir)
if dir == "" {
dir = "."
}
return filepath.Join(dir, filepath.FromSlash(slashClean(name)))
}
func (d WebDavDir) Mkdir(ctx context.Context, name string, perm os.FileMode) error {
if name = d.resolve(name); name == "" {
return os.ErrNotExist
}
return d.panClientProxy.Mkdir(name, perm)
}
func (d WebDavDir) OpenFile(ctx context.Context, name string, flag int, perm os.FileMode) (webdav.File, error) {
if name == "" {
2021-12-31 19:55:00 +08:00
return &WebDavFile{
2021-12-30 20:24:33 +08:00
panClientProxy: d.panClientProxy,
nameSnapshot: d.fileInfo,
childrenSnapshot: nil,
listPos: 0,
readPos: 0,
writePos: 0,
}, nil
}
fileItem,e := d.panClientProxy.FileInfoByPath(d.formatAbsoluteName(name))
if e != nil {
logger.Verboseln("OpenFile failed, file path not existed: " + d.formatAbsoluteName(name))
return nil, e
}
wdfi := NewWebDavFileInfo(fileItem)
wdfi.fullPath = d.formatAbsoluteName(name)
2021-12-31 19:55:00 +08:00
return &WebDavFile{
2021-12-30 20:24:33 +08:00
panClientProxy: d.panClientProxy,
nameSnapshot: wdfi,
childrenSnapshot: nil,
listPos: 0,
readPos: 0,
writePos: 0,
2021-12-31 19:55:00 +08:00
sessionId: d.getSessionId(ctx),
2021-12-30 20:24:33 +08:00
}, nil
}
func (d WebDavDir) RemoveAll(ctx context.Context, name string) error {
if name = d.resolve(name); name == "" {
return os.ErrNotExist
}
if name == filepath.Clean(string(d.Dir)) {
// Prohibit removing the virtual root directory.
return os.ErrInvalid
}
2022-01-01 11:14:21 +08:00
return d.panClientProxy.RemoveAll(name)
2021-12-30 20:24:33 +08:00
}
func (d WebDavDir) Rename(ctx context.Context, oldName, newName string) error {
if oldName = d.resolve(oldName); oldName == "" {
return os.ErrNotExist
}
if newName = d.resolve(newName); newName == "" {
return os.ErrNotExist
}
if root := filepath.Clean(string(d.Dir)); root == oldName || root == newName {
// Prohibit renaming from or to the virtual root directory.
return os.ErrInvalid
}
2021-12-30 23:15:42 +08:00
if path.Dir(oldName) == path.Dir(newName) {
// rename
return d.panClientProxy.Rename(oldName, newName)
} else {
// move file
return d.panClientProxy.Move(oldName, newName)
}
2021-12-30 20:24:33 +08:00
}
func (d WebDavDir) Stat(ctx context.Context, name string) (os.FileInfo, error) {
f := &d.fileInfo
if name != "" {
fileItem,e := d.panClientProxy.FileInfoByPath(d.formatAbsoluteName(name))
if e != nil {
logger.Verboseln("file path not existed: " + d.formatAbsoluteName(name))
2021-12-30 23:06:02 +08:00
return nil, os.ErrNotExist
2021-12-30 20:24:33 +08:00
}
*f = NewWebDavFileInfo(fileItem)
}
return f, nil
}
// WebDavFile 文件实例
type WebDavFile struct {
webdav.File
2022-01-01 09:20:46 +08:00
// 网盘Client
2021-12-30 20:24:33 +08:00
panClientProxy *PanClientProxy
2022-01-01 09:20:46 +08:00
// nameSnapshot 文件关联的网盘文件信息
2021-12-30 20:24:33 +08:00
nameSnapshot WebDavFileInfo
2022-01-01 09:20:46 +08:00
// childrenSnapshot 目录下的子文件信息列表
2021-12-30 20:24:33 +08:00
childrenSnapshot []WebDavFileInfo
listPos int
2022-01-01 09:20:46 +08:00
// 读取偏移值
2021-12-30 20:24:33 +08:00
readPos int64
2022-01-01 09:20:46 +08:00
// 写入偏移值
2021-12-30 20:24:33 +08:00
writePos int64
2021-12-31 19:55:00 +08:00
2022-01-01 09:20:46 +08:00
// 会话ID
2021-12-31 19:55:00 +08:00
sessionId string
2021-12-30 20:24:33 +08:00
}
2021-12-31 19:55:00 +08:00
func (f *WebDavFile) Close() error {
2021-12-31 00:21:53 +08:00
f.readPos = 0
f.writePos = 0
2021-12-30 20:24:33 +08:00
return nil
}
2021-12-31 19:55:00 +08:00
func (f *WebDavFile) Read(p []byte) (int, error) {
count, err := f.panClientProxy.DownloadFilePart(f.sessionId, f.nameSnapshot.fileId, f.readPos, p)
2021-12-31 00:21:53 +08:00
if err != nil {
return 0, err
}
f.readPos += int64(count)
return count, nil
2021-12-30 20:24:33 +08:00
}
// Readdir 获取文件目录
2021-12-31 19:55:00 +08:00
func (f *WebDavFile) Readdir(count int) (fis []os.FileInfo, err error) {
2021-12-30 20:24:33 +08:00
if f.childrenSnapshot == nil || len(f.childrenSnapshot) == 0 {
fileList, e := f.panClientProxy.FileListGetAll(f.nameSnapshot.fullPath)
if e != nil {
return nil, e
}
for _,fileItem := range fileList {
wdfi := NewWebDavFileInfo(fileItem)
wdfi.fullPath = f.nameSnapshot.fullPath + "/" + wdfi.name
f.childrenSnapshot = append(f.childrenSnapshot, wdfi)
}
}
realCount := count
if (f.listPos + realCount) > len(f.childrenSnapshot) {
realCount = len(f.childrenSnapshot) - f.listPos
}
if realCount == 0 {
realCount = len(f.childrenSnapshot)
}
fis = make([]os.FileInfo, realCount)
idx := 0
for idx < realCount {
fis[idx] = &f.childrenSnapshot[f.listPos + idx]
idx ++
}
return fis, nil
}
2021-12-31 19:55:00 +08:00
func (f *WebDavFile) Seek(offset int64, whence int) (int64, error) {
var abs int64
switch whence {
case io.SeekStart:
abs = offset
logger.Verboseln(f.sessionId + " SeekStart offset = " + strconv.Itoa(int(offset)) + " return offset = " + strconv.Itoa(int(abs)))
case io.SeekCurrent:
abs = f.readPos + offset
logger.Verboseln(f.sessionId + " SeekCurrent offset = " + strconv.Itoa(int(offset)) + " return offset = " + strconv.Itoa(int(abs)))
case io.SeekEnd:
abs = f.nameSnapshot.size + offset
logger.Verboseln(f.sessionId + " SeekEnd offset = " + strconv.Itoa(int(offset)) + " return offset = " + strconv.Itoa(int(abs)))
default:
return 0, errors.New("invalid whence")
2021-12-31 00:21:53 +08:00
}
2021-12-31 19:55:00 +08:00
if abs < 0 {
return 0, os.ErrInvalid
}
f.readPos = abs
return f.readPos, nil
2021-12-30 20:24:33 +08:00
}
2021-12-31 19:55:00 +08:00
func (f *WebDavFile) Stat() (os.FileInfo, error) {
2021-12-30 20:24:33 +08:00
return &f.nameSnapshot, nil
}
2021-12-31 19:55:00 +08:00
func (f *WebDavFile) Write(p []byte) (int, error) {
2021-12-30 20:24:33 +08:00
return 0, nil
}
// WebDavFileInfo 文件信息
type WebDavFileInfo struct {
os.FileInfo
fileId string
name string
size int64
mode os.FileMode
modTime time.Time
fullPath string
}
func NewWebDavFileInfo(fileItem *aliyunpan.FileEntity) WebDavFileInfo {
var LOC, _ = time.LoadLocation("Asia/Shanghai")
t,_ := time.ParseInLocation("2006-01-02 15:04:05", fileItem.UpdatedAt, LOC)
fm := os.FileMode(0)
if fileItem.IsFolder() {
fm = os.ModeDir
}
return WebDavFileInfo{
fileId: fileItem.FileId,
name: fileItem.FileName,
size: fileItem.FileSize,
mode: fm,
modTime: t,
fullPath: fileItem.Path,
}
}
func (f *WebDavFileInfo) Name() string { return f.name }
func (f *WebDavFileInfo) Size() int64 { return f.size }
func (f *WebDavFileInfo) Mode() os.FileMode { return f.mode }
func (f *WebDavFileInfo) ModTime() time.Time { return f.modTime }
func (f *WebDavFileInfo) IsDir() bool { return f.mode.IsDir() }
func (f *WebDavFileInfo) Sys() interface{} { return nil }
func (f *WebDavFileInfo) ContentType(ctx context.Context) (contentType string, err error) {
if mimeType := mime.TypeByExtension(path.Ext(f.Name())); mimeType != "" {
// We can figure out the mime from the extension.
return mimeType, nil
} else {
// We can't figure out the mime type without sniffing, call it an octet stream.
return "application/octet-stream", nil
}
}