Compare commits

...

6 Commits

Author SHA1 Message Date
xiaoyaofenfen d05d19acd8 Merge pull request #548 from qinweixian/patch-1
修复后台上传脚本bug
2026-09-07 09:29:06 +08:00
秦伟先 5179f914ed 修复后台上传脚本bug 2026-09-03 10:42:52 +08:00
tickstep 2feaa3cf82 update readme file 2026-07-05 09:42:32 +08:00
tickstep studio team cedd43d402 update version to v0.4.0 2026-06-29 10:42:16 +08:00
tickstep studio team e62bdbb9f1 refractor OSS url parser 2026-06-29 10:27:00 +08:00
tickstep studio team acc408f0a0 fix download url expired calc error issue #544 #543 #542 2026-06-29 09:29:05 +08:00
10 changed files with 117 additions and 38 deletions
+7 -7
View File
@@ -66,9 +66,9 @@
参考例子:
```shell
wget https://github.com/tickstep/aliyunpan/releases/download/v0.3.9/aliyunpan-v0.3.9-linux-amd64.zip
unzip aliyunpan-v0.3.9-linux-amd64.zip
cd aliyunpan-v0.3.9-linux-amd64
wget https://github.com/tickstep/aliyunpan/releases/download/v0.4.0/aliyunpan-v0.4.0-linux-amd64.zip
unzip aliyunpan-v0.4.0-linux-amd64.zip
cd aliyunpan-v0.4.0-linux-amd64
./aliyunpan
```
@@ -107,7 +107,7 @@ winget source update
```
安装
```powershell
winget install tickstep.aliyunpan --silent
winget install tickstep.aliyunpan --source winget --silent
```
@@ -126,7 +126,7 @@ winget install tickstep.aliyunpan --silent
![](./assets/images/sync_command-basic_logic.jpg)
```
docker run -d --name=aliyunpan-sync --restart=always -v "<your aliyunpan_config.json>:/home/app/config/aliyunpan_config.json" -v "<your local dir>:/home/app/data" -e ALIYUNPAN_PAN_DIR="<your drive pan dir>" -e ALIYUNPAN_SYNC_MODE="upload" -e ALIYUNPAN_SYNC_POLICY="increment" -e ALIYUNPAN_SYNC_DRIVE="backup" -e ALIYUNPAN_SYNC_LOG="true" tickstep/aliyunpan-sync:v0.3.9
docker run -d --name=aliyunpan-sync --restart=always -v "<your aliyunpan_config.json>:/home/app/config/aliyunpan_config.json" -v "<your local dir>:/home/app/data" -e ALIYUNPAN_PAN_DIR="<your drive pan dir>" -e ALIYUNPAN_SYNC_MODE="upload" -e ALIYUNPAN_SYNC_POLICY="increment" -e ALIYUNPAN_SYNC_DRIVE="backup" -e ALIYUNPAN_SYNC_LOG="true" tickstep/aliyunpan-sync:v0.4.0
<your aliyunpan_config.json>: 用户已经登录成功并保存好的aliyunpan_config.json凭据文件
<your local dir>:本地目标目录,绝对路径,例如:/tickstep/Documents/设计文档
@@ -241,7 +241,7 @@ aliyunpan:/ tickstep$ ls
```
### 下载文件
通过 `aliyunpan config set -savedir <savedir>` 可以自定义下载文件保存的目录。
如果需要更改默认下载目录路径,可以通过 `aliyunpan config set -savedir <savedir>` 自定义下载文件保存的目录。
```shell
aliyunpan:/ tickstep$ download IMG_0106.JPG
@@ -320,7 +320,7 @@ aliyunpan:/ tickstep$ upload /Users/tickstep/Downloads /photo -ui
```
### 同步备份文件
同步备份功能,支持备份本地文件到云盘,备份云盘文件到本地,双向同步备份三种模式。支持JavaScript插件对备份文件进行过滤。
同步备份功能,支持备份本地文件到云盘,备份云盘文件到本地两种模式。支持JavaScript插件对备份文件进行过滤。
例如:将本地目录 `/tickstep/Documents/设计文档` 中的文件备份上传到云盘目录 `/备份盘/我的文档`
```shell
+1 -1
View File
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
<assemblyIdentity version="0.3.9.0" processorArchitecture="*" name="com.tickstep.aliyunpan" type="win32"/>
<assemblyIdentity version="0.4.0.0" processorArchitecture="*" name="com.tickstep.aliyunpan" type="win32"/>
<dependency>
<dependentAssembly>
<assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="*" publicKeyToken="6595b64144ccf1df" language="*"/>
+26 -10
View File
@@ -343,20 +343,36 @@ aliyunpan upload -exn "\.jpg$" -exn "\.mp3$" C:/Users/Administrator/Video /视
```
#!/bin/bash
# 是否开启调试日志
# 是否开启调试日志 (1=开启, 0=关闭)
export ALIYUNPAN_VERBOSE=0
# (可选)配置目录的绝对路径(请更改成你自己的目录)
export ALIYUNPAN_CONFIG_DIR=/Users/tickstep/Applications/adrive/config
# aliyunpan程序所在的绝对路径(请更改成你自己的目录)
export ALIYUNPAN_BIN=/Users/tickstep/Applications/adrive/aliyunpan
# 本地目录(请更改成你自己的目录)
# (可选)配置目录的绝对路径
export ALIYUNPAN_CONFIG_DIR="/Users/tickstep/Applications/adrive/config"
# aliyunpan程序所在的绝对路径
export ALIYUNPAN_BIN="/Users/tickstep/Applications/adrive/aliyunpan"
# 本地目录
LOCAL_DIR="/tickstep/Documents/我的文档"
# 网盘目录(请更改成你自己的目录)
# 网盘目录
PAN_DIR="/我的文档"
# 执行上传
$ALIYUNPAN_BIN upload -exn "^\." -exn "^@eadir$" "$LOCAL_DIR" $PAN_DIR"
# 检查本地目录是否存在
if [ ! -d "$LOCAL_DIR" ]; then
echo "[ERROR] 本地目录不存在: $LOCAL_DIR"
exit 1
fi
# 执行上传(已修复引号、添加安全引用和覆盖策略)
"$ALIYUNPAN_BIN" upload \
-exn "^\." \
-exn "^@eadir$" \
-ow \
"$LOCAL_DIR" \
"$PAN_DIR"
echo "[INFO] 上传任务执行完毕"
```
增加脚本执行权限
@@ -774,4 +790,4 @@ Windows
### 第二步
打开aliyunpan命令行程序,任何云盘命令都有类似如下日志输出
![](../assets/images/debug-log-screenshot.png)
![](../assets/images/debug-log-screenshot.png)
+7 -5
View File
@@ -15,8 +15,6 @@ package downloader
import (
"errors"
"github.com/tickstep/library-go/logger"
"github.com/tickstep/library-go/requester"
mathrand "math/rand"
"mime"
"net/url"
@@ -24,6 +22,10 @@ import (
"regexp"
"strconv"
"time"
"github.com/tickstep/aliyunpan/internal/utils"
"github.com/tickstep/library-go/logger"
"github.com/tickstep/library-go/requester"
)
var (
@@ -102,15 +104,15 @@ func fixCacheSize(size *int) {
// IsUrlExpired 下载链接是否已过期。过期返回True
func IsUrlExpired(urlStr string) bool {
u, err := url.Parse(urlStr)
expiredTimeSec, err := utils.GetExpiredTimeSecFromOSSUrl(urlStr)
if err != nil {
// 解析错误,默认过期
return true
}
expiredTimeSecStr := u.Query().Get("x-oss-expires")
expiredTimeSec, _ := strconv.ParseInt(expiredTimeSecStr, 10, 64)
if (expiredTimeSec - time.Now().Unix()) <= 5 { // 小于5秒钟
// expired
return true
}
// 返回未过期
return false
}
+8 -8
View File
@@ -16,15 +16,15 @@ package panupload
import (
"crypto/sha1"
"encoding/hex"
"github.com/tickstep/aliyunpan/internal/config"
"github.com/tickstep/library-go/converter"
"github.com/tickstep/library-go/logger"
"net/url"
"os"
"path"
"strconv"
"strings"
"time"
"github.com/tickstep/aliyunpan/internal/config"
"github.com/tickstep/aliyunpan/internal/utils"
"github.com/tickstep/library-go/converter"
"github.com/tickstep/library-go/logger"
)
const (
@@ -55,16 +55,16 @@ func getBlockSize(fileSize int64) int64 {
// IsUrlExpired 上传链接是否已过期。过期返回True
func IsUrlExpired(urlStr string) bool {
u, err := url.Parse(urlStr)
expiredTimeSec, err := utils.GetExpiredTimeSecFromOSSUrl(urlStr)
if err != nil {
// 解析错误,默认过期
return true
}
expiredTimeSecStr := u.Query().Get("x-oss-expires")
expiredTimeSec, _ := strconv.ParseInt(expiredTimeSecStr, 10, 64)
if (expiredTimeSec - time.Now().Unix()) <= 300 { // 小于5分钟
// expired
return true
}
// 返回未过期
return false
}
+61
View File
@@ -383,3 +383,64 @@ func GetPublicIP() (string, error) {
ip := strings.TrimSpace(strings.Split(ipResp.Origin, ",")[0])
return ip, nil
}
// GetExpiredTimeSecFromOSSUrl 从OSS URL中解析出统一的过期时间戳(秒)
func GetExpiredTimeSecFromOSSUrl(rawURL string) (int64, error) {
parsedURL, err := url.Parse(rawURL)
if err != nil {
return 0, fmt.Errorf("failed to parse URL: %w", err)
}
query := parsedURL.Query()
sigVersion := query.Get("x-oss-signature-version")
var expires int64 = 0
// 检测版本标识(不区分大小写)
if strings.Contains(sigVersion, "OSS4") {
// V4 签名:x-oss-expires 为相对秒数,需要结合 x-oss-date
expiresStr := query.Get("x-oss-expires")
if expiresStr == "" {
return 0, fmt.Errorf("V4 signature: 'x-oss-expires' parameter not found")
}
relSec, err := strconv.ParseInt(expiresStr, 10, 64)
if err != nil {
return 0, fmt.Errorf("V4 signature: invalid 'x-oss-expires' value: %w", err)
}
dateStr := query.Get("x-oss-date")
if dateStr == "" {
return 0, fmt.Errorf("V4 signature: 'x-oss-date' parameter not found")
}
// 解析 ISO8601 UTC 时间,例如 "20231220T083018Z"
t, err := time.Parse("20060102T150405Z", dateStr)
if err != nil {
return 0, fmt.Errorf("V4 signature: failed to parse 'x-oss-date': %w", err)
}
expires = t.Unix() + relSec
} else if strings.Contains(sigVersion, "OSS3") || strings.Contains(sigVersion, "OSS2") {
// V2 / V3:x-oss-expires 为绝对时间戳
expiresStr := query.Get("x-oss-expires")
if expiresStr == "" {
return 0, fmt.Errorf("V2/V3 signature: 'x-oss-expires' parameter not found")
}
expires, err = strconv.ParseInt(expiresStr, 10, 64)
if err != nil {
return 0, fmt.Errorf("V2/V3 signature: invalid 'x-oss-expires' value: %w", err)
}
} else {
// 默认为 V1 签名(没有 x-oss-signature-version 或为空)
expiresStr := query.Get("Expires")
if expiresStr == "" {
return 0, fmt.Errorf("V1 signature: 'Expires' parameter not found")
}
expires, err = strconv.ParseInt(expiresStr, 10, 64)
if err != nil {
return 0, fmt.Errorf("V1 signature: invalid 'Expires' value: %w", err)
}
}
return expires, nil
}
+1 -1
View File
@@ -54,7 +54,7 @@ const (
var (
// Version 版本号
Version = "v0.3.9"
Version = "v0.4.0"
// 命令历史文件
historyFilePath = filepath.Join(config.GetConfigDir(), "aliyunpan_command_history.txt")
Binary file not shown.
Binary file not shown.
+6 -6
View File
@@ -2,14 +2,14 @@
"FixedFileInfo": {
"FileVersion": {
"Major": 0,
"Minor": 3,
"Patch": 9,
"Minor": 4,
"Patch": 0,
"Build": 0
},
"ProductVersion": {
"Major": 0,
"Minor": 3,
"Patch": 9,
"Minor": 4,
"Patch": 0,
"Build": 0
},
"FileFlagsMask": "3f",
@@ -22,14 +22,14 @@
"Comments": "",
"CompanyName": "tickstep",
"FileDescription": "阿里云盘客户端",
"FileVersion": "v0.3.9",
"FileVersion": "v0.4.0",
"InternalName": "",
"LegalCopyright": "© 2021-2026 tickstep.",
"LegalTrademarks": "",
"OriginalFilename": "",
"PrivateBuild": "",
"ProductName": "aliyunpan",
"ProductVersion": "v0.3.9",
"ProductVersion": "v0.4.0",
"SpecialBuild": ""
},
"VarFileInfo": {