jmalcloud/upload.sh
chenpei c26105b684 初始提交:JmalCloud公共空间版改造完成
- 改造为公共空间模式,所有用户共享文件
- 移除用户文件隔离机制
- 保持前端API兼容性
- 更新配置文件和文档
- 创建完整启动脚本工具链
2025-08-24 11:16:01 +08:00

319 lines
9.9 KiB
Bash
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/bin/bash
# 默认配置
# 文件分片大小(1MB = 1048576)
CHUNK_SIZE=1048576
# 1. 服务端地址,如果使用默认的docker-compose部署,则为: http://ip:7072, 或者 http://ip:7070/api
SERVER_URL=${SERVER_URL:-""}
# 2. 在设置-授权应用中添加一个访问令牌
ACCESS_TOKEN=${ACCESS_TOKEN:-""}
# 3. 用户账号,不是用户名
USERNAME=${USERNAME:-""}
# 4. 要上传到那个目录,根目录为"/"
CURRENT_DIRECTORY=${CURRENT_DIRECTORY:-""}
# 检查必要配置
check_config() {
local missing_configs=()
if [ -z "$SERVER_URL" ]; then
missing_configs+=("SERVER_URL")
fi
if [ -z "$ACCESS_TOKEN" ]; then
missing_configs+=("ACCESS_TOKEN")
fi
if [ -z "$USERNAME" ]; then
missing_configs+=("USERNAME")
fi
if [ -z "$CURRENT_DIRECTORY" ]; then
missing_configs+=("CURRENT_DIRECTORY")
fi
if [ ${#missing_configs[@]} -ne 0 ]; then
echo "错误: 以下必要配置缺失:"
printf '%s\n' "${missing_configs[@]}"
echo
echo "请通过以下方式之一进行配置:"
echo "1. 设置环境变量:"
for config in "${missing_configs[@]}"; do
echo " export $config=<value>"
done
echo
echo "2. 直接修改脚本中的默认值:"
for config in "${missing_configs[@]}"; do
echo " $config=\"<value>\""
done
exit 1
fi
# 验证服务器连接和访问令牌
local response
response=$(curl -s -w "\n%{http_code}" \
--connect-timeout 3 \
--max-time 3 \
"${SERVER_URL}/api/user/info" \
--header "access-token: ${ACCESS_TOKEN}")
local http_code=$(echo "$response" | tail -n1)
local body=$(echo "$response" | sed '$d')
if [ "$http_code" != "200" ]; then
echo "错误: 服务器连接失败"
echo "请检查 SERVER_URL 是否正确"
exit 1
fi
# 检查响应中的 code 字段
if ! echo "$body" | grep -q '"code":0'; then
echo "请检查 ACCESS_TOKEN 是否正确"
exit 1
fi
}
# 检查命令行参数
if [ $# -ne 1 ]; then
echo "用法: $0 <文件或目录路径>"
exit 1
fi
PATH_TO_UPLOAD="$1"
# 检查路径是否存在
if [ ! -e "$PATH_TO_UPLOAD" ]; then
echo "错误: 路径不存在 - $PATH_TO_UPLOAD"
exit 1
fi
# 检查配置
check_config
# URL编码函数
url_encode() {
local string="$1"
echo -n "$string" | xxd -p | sed 's/\(..\)/%\1/g'
}
# 获取文件大小的函数,适配 Linux 和 macOS
get_file_size() {
local file_path="$1"
if [[ "$OSTYPE" == "darwin"* ]]; then
# macOS
stat -f%z "$file_path"
else
# Linux
stat -c%s "$file_path"
fi
}
# 进度条
show_progress() {
local progress=$1
local width=50
local percent=$((progress * width / 100))
printf "\r"
printf "上传进度: ["
for ((i=0; i<width; i++)); do
if [ $i -lt $percent ]; then
printf "#"
else
printf " "
fi
done
printf "] %3d%%" $progress
}
# 文件上传
upload_chunk() {
local chunk_file="$1"
local chunk_number="$2"
local current_chunk_size="$3"
# URL编码文件名和路径
local encoded_filename=$(url_encode "$FILE_NAME")
local encoded_relativepath=$(url_encode "$FILE_NAME")
local encoded_directory=$(url_encode "$CURRENT_DIRECTORY")
local check_url="${SERVER_URL}/upload?chunkNumber=${chunk_number}&chunkSize=${CHUNK_SIZE}&currentChunkSize=${current_chunk_size}&totalSize=${TOTAL_SIZE}&identifier=${IDENTIFIER}&filename=${encoded_filename}&relativePath=${encoded_relativepath}&totalChunks=${TOTAL_CHUNKS}&isFolder=false&currentDirectory=${encoded_directory}&username=${USERNAME}"
local check_response=$(curl -s -H "access-token: ${ACCESS_TOKEN}" -H "lang: zh-CN" "$check_url")
if echo "$check_response" | grep -q '"upload":true'; then
curl -s --location --request POST "${SERVER_URL}/upload" \
--header "access-token: ${ACCESS_TOKEN}" \
--form "chunkNumber=$chunk_number" \
--form "chunkSize=$CHUNK_SIZE" \
--form "currentChunkSize=$current_chunk_size" \
--form "totalSize=$TOTAL_SIZE" \
--form "identifier=$IDENTIFIER" \
--form "filename=$encoded_filename" \
--form "relativePath=$encoded_relativepath" \
--form "totalChunks=$TOTAL_CHUNKS" \
--form "isFolder=false" \
--form "currentDirectory=$encoded_directory" \
--form "username=$USERNAME" \
--form "file=@$chunk_file"
if [ $? -ne 0 ]; then
return 1
fi
fi
return 0
}
# 文件夹上传
upload_directory() {
local dir_path="$1"
local base_dir="$2"
local original_current_directory="$CURRENT_DIRECTORY"
local dir_name=$(basename "$dir_path")
local total_files=$(find "$dir_path" -type f | wc -l)
local current_file=0
echo "开始上传文件夹: $dir_name (共 $total_files 个文件)"
find "$dir_path" -type f | while read file_path; do
current_file=$((current_file + 1))
local rel_path=${file_path#$base_dir/}
local file_dir=$(dirname "$rel_path")
# 设置并编码当前目录路径
if [ "$file_dir" = "." ]; then
CURRENT_DIRECTORY="${original_current_directory}/${dir_name}"
else
CURRENT_DIRECTORY="${original_current_directory}/${file_dir}"
fi
echo "正在上传 ($current_file/$total_files): $(basename "$file_path") -> ${CURRENT_DIRECTORY}"
FILE_PATH="$file_path"
FILE_NAME=$(basename "$file_path")
TOTAL_SIZE=$(get_file_size "$FILE_PATH")
IDENTIFIER="${TOTAL_SIZE}-${FILE_NAME}"
IDENTIFIER=$(echo "$IDENTIFIER" | tr -cd '[:alnum:]-')
# URL编码相关变量
local encoded_filename=$(url_encode "$FILE_NAME")
local encoded_directory=$(url_encode "$CURRENT_DIRECTORY")
if [ $TOTAL_SIZE -le $CHUNK_SIZE ]; then
TOTAL_CHUNKS=1
upload_chunk "$FILE_PATH" 1 "$TOTAL_SIZE" > /dev/null
if [ $? -ne 0 ]; then
echo "文件上传失败: $FILE_NAME"
continue
fi
else
TOTAL_CHUNKS=$(( ($TOTAL_SIZE + $CHUNK_SIZE - 1) / $CHUNK_SIZE ))
TEMP_DIR=$(mktemp -d)
split -b $CHUNK_SIZE "$FILE_PATH" "$TEMP_DIR/chunk."
CHUNK_INDEX=1
for CHUNK_FILE in "$TEMP_DIR"/chunk.*; do
CURRENT_CHUNK_SIZE=$(get_file_size "$CHUNK_FILE")
upload_chunk "$CHUNK_FILE" "$CHUNK_INDEX" "$CURRENT_CHUNK_SIZE" > /dev/null
if [ $? -ne 0 ]; then
echo "分片上传失败: $FILE_NAME"
rm -rf "$TEMP_DIR"
continue 2
fi
CHUNK_INDEX=$((CHUNK_INDEX + 1))
done
rm -rf "$TEMP_DIR"
# 合并文件时使用编码后的参数
MERGE_URL="${SERVER_URL}/merge"
MERGE_PARAMS="filename=${encoded_filename}&relativePath=${encoded_filename}&identifier=${IDENTIFIER}&currentDirectory=${encoded_directory}&username=${USERNAME}&totalSize=${TOTAL_SIZE}&isFolder=false"
MERGE_RESPONSE=$(curl -s -X POST "${MERGE_URL}?${MERGE_PARAMS}" \
-H "access-token: ${ACCESS_TOKEN}" \
-H "lang: zh-CN")
if ! echo "$MERGE_RESPONSE" | grep -q '"code":0'; then
echo "文件合并失败: $FILE_NAME"
echo "错误响应: $MERGE_RESPONSE"
continue
fi
fi
echo "文件上传完成: $FILE_NAME"
done
CURRENT_DIRECTORY="$original_current_directory"
}
if [ -d "$PATH_TO_UPLOAD" ]; then
ABSOLUTE_PATH=$(cd "$(dirname "$PATH_TO_UPLOAD")" && pwd)
upload_directory "$PATH_TO_UPLOAD" "$ABSOLUTE_PATH"
echo "文件夹上传完成"
else
FILE_PATH="$PATH_TO_UPLOAD"
FILE_NAME=$(basename "$FILE_PATH")
TOTAL_SIZE=$(get_file_size "$FILE_PATH")
IDENTIFIER="${TOTAL_SIZE}-${FILE_NAME}"
IDENTIFIER=$(echo "$IDENTIFIER" | tr -cd '[:alnum:]-')
echo "开始上传文件: $FILE_NAME (大小: $TOTAL_SIZE 字节)"
if [ $TOTAL_SIZE -le $CHUNK_SIZE ]; then
# 小文件直接上传
TOTAL_CHUNKS=1
upload_chunk "$FILE_PATH" 1 "$TOTAL_SIZE" > /dev/null
if [ $? -ne 0 ]; then
echo "文件上传失败"
exit 1
fi
else
# 大文件分片上传
TOTAL_CHUNKS=$(( ($TOTAL_SIZE + $CHUNK_SIZE - 1) / $CHUNK_SIZE ))
TEMP_DIR=$(mktemp -d)
split -b $CHUNK_SIZE "$FILE_PATH" "$TEMP_DIR/chunk."
echo "开始分片上传..."
CHUNK_INDEX=1
for CHUNK_FILE in "$TEMP_DIR"/chunk.*; do
CURRENT_CHUNK_SIZE=$(get_file_size "$CHUNK_FILE")
PROGRESS=$((CHUNK_INDEX * 100 / TOTAL_CHUNKS))
show_progress $PROGRESS
upload_chunk "$CHUNK_FILE" "$CHUNK_INDEX" "$CURRENT_CHUNK_SIZE" > /dev/null
if [ $? -ne 0 ]; then
echo -e "\n分片上传失败"
rm -rf "$TEMP_DIR"
exit 1
fi
CHUNK_INDEX=$((CHUNK_INDEX + 1))
done
rm -rf "$TEMP_DIR"
echo -e "\n分片上传完成"
# 合并文件
echo "开始合并文件..."
# URL编码文件名和目录
encoded_filename=$(url_encode "$FILE_NAME")
encoded_directory=$(url_encode "$CURRENT_DIRECTORY")
MERGE_URL="${SERVER_URL}/merge"
MERGE_PARAMS="filename=${encoded_filename}&relativePath=${encoded_filename}&identifier=${IDENTIFIER}&currentDirectory=${encoded_directory}&username=${USERNAME}&totalSize=${TOTAL_SIZE}&isFolder=false"
MERGE_RESPONSE=$(curl -s -X POST "${MERGE_URL}?${MERGE_PARAMS}" \
-H "access-token: ${ACCESS_TOKEN}" \
-H "lang: zh-CN")
if ! echo "$MERGE_RESPONSE" | grep -q '"code":0'; then
echo "文件合并失败"
echo "错误响应: $MERGE_RESPONSE"
exit 1
fi
fi
echo "文件上传完成"
fi