给我写一个文件夹和文件同步到webdav的go程序,有新的文件夹或者新的文件,实时同步到webdav,新建一个日志,每一次文件和文件夹上传成功则把日志更新到本地的日志文本当中。0.1秒就扫描一次是否有新的文件夹或者文件,如果有就更新,没有就不更新。文件和文件夹上传的时候并发上传,文件上传成功后自动删除
以下是一个简单的实现,使用了Go内置的os和net/http包以及第三方包go-webdav/webdav:
package main
import (
"fmt"
"io/ioutil"
"log"
"net/http"
"os"
"strings"
"sync"
"time"
"github.com/studio-b12/go-webdav"
)
const (
localDir = "/path/to/local/dir"
remoteDir = "/remote/webdav/dir"
logFile = "/path/to/log/file"
interval = 100 * time.Millisecond
)
var (
client *webdav.Client
logger *log.Logger
wg sync.WaitGroup
)
func init() {
// 初始化WebDAV客户端
url := "https://webdav.example.com" // WebDAV服务器地址
username := "username" // WebDAV用户名
password := "password" // WebDAV密码
client = webdav.NewClient(url, username, password)
// 初始化日志
file, err := os.OpenFile(logFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
log.Fatalf("failed to open log file: %v", err)
}
logger = log.New(file, "", log.LstdFlags)
}
func main() {
// 启动文件同步协程
go syncFiles()
// 启动HTTP服务器,用于上传文件
http.HandleFunc("/upload", uploadHandler)
if err := http.ListenAndServe(":8080", nil); err != nil {
log.Fatalf("failed to start HTTP server: %v", err)
}
}
func syncFiles() {
// 初始化已同步的文件列表
syncedFiles := make(map[string]struct{})
syncedDirs := make(map[string]struct{})
for {
// 扫描本地目录
files, err := ioutil.ReadDir(localDir)
if err != nil {
logger.Printf("failed to read local dir: %v", err)
continue
}
// 比较本地和远程文件列表,找出新增的文件和文件夹
var newFiles []os.FileInfo
var newDirs []os.FileInfo
for _, file := range files {
path := file.Name()
if _, ok := syncedFiles[path]; !ok && !file.IsDir() {
newFiles = append(newFiles, file)
}
if _, ok := syncedDirs[path]; !ok && file.IsDir() {
newDirs = append(newDirs, file)
}
}
// 上传新增的文件
for _, file := range newFiles {
wg.Add(1)
go func(file os.FileInfo) {
defer wg.Done()
path := file.Name()
localPath := localDir + "/" + path
remotePath := remoteDir + "/" + path
// 上传文件
f, err := os.Open(localPath)
if err != nil {
logger.Printf("failed to open local file %s: %v", path, err)
return
}
defer f.Close()
if err := client.WriteStream(remotePath, f, false); err != nil {
logger.Printf("failed to upload file %s: %v", path, err)
return
}
// 记录日志
logger.Printf("uploaded file %s", path)
// 标记文件已同步
syncedFiles[path] = struct{}{}
// 删除本地文件
if err := os.Remove(localPath); err != nil {
logger.Printf("failed to delete local file %s: %v", path, err)
}
}(file)
}
// 上传新增的文件夹
for _, dir := range newDirs {
wg.Add(1)
go func(dir os.FileInfo) {
defer wg.Done()
path := dir.Name()
remotePath := remoteDir + "/" + path
// 创建远程文件夹
if err := client.Mkdir(remotePath, 0755); err != nil {
logger.Printf("failed to create remote dir %s: %v", path, err)
return
}
// 记录日志
logger.Printf("created remote dir %s", path)
// 标记文件夹已同步
syncedDirs[path] = struct{}{}
}(dir)
}
// 等待所有上传任务完成
wg.Wait()
// 暂停一段时间后继续扫描
time.Sleep(interval)
}
}
func uploadHandler(w http.ResponseWriter, r *http.Request) {
// 解析上传的文件
r.ParseMultipartForm(32 << 20) // 32MB
file, handler, err := r.FormFile("file")
if err != nil {
http.Error(w, "failed to read file from request", http.StatusBadRequest)
return
}
defer file.Close()
// 上传文件
remotePath := remoteDir + "/" + handler.Filename
if err := client.WriteStream(remotePath, file, false); err != nil {
http.Error(w, fmt.Sprintf("failed to upload file %s: %v", handler.Filename, err), http.StatusInternalServerError)
return
}
// 记录日志
logger.Printf("uploaded file %s", handler.Filename)
fmt.Fprintf(w, "OK")
}
说明:
- 在
init函数中初始化WebDAV客户端和日志对象。 - 在
main函数中启动文件同步协程和HTTP服务器。 - 文件同步协程的主要逻辑如下:
- 扫描本地目录,找出新增的文件和文件夹。
- 对于新增的文件,启动一个上传文件的goroutine,上传文件到WebDAV服务器,上传完成后标记文件已同步,记录日志,并删除本地文件。
- 对于新增的文件夹,创建远程文件夹,创建完成后标记文件夹已同步,记录日志。
- 暂停一段时间后继续扫描。
- HTTP服务器的主要逻辑是解析上传的文件并上传到WebDAV服务器,上传完成后返回一个OK响应。
原文地址: https://www.cveoy.top/t/topic/w6p 著作权归作者所有。请勿转载和采集!