Compare commits
No commits in common. "main" and "temp" have entirely different histories.
|
@ -1,75 +0,0 @@
|
||||||
# This is a basic workflow to help you get started with Actions
|
|
||||||
|
|
||||||
name: aliyundriver-Build
|
|
||||||
|
|
||||||
# Controls when the action will run.
|
|
||||||
on:
|
|
||||||
# Triggers the workflow on push or pull request events but only for the main branch
|
|
||||||
push:
|
|
||||||
branches: [ main ]
|
|
||||||
pull_request:
|
|
||||||
branches: [ main ]
|
|
||||||
# schedule:
|
|
||||||
# - cron: "0 0 * * */3"
|
|
||||||
|
|
||||||
# Allows you to run this workflow manually from the Actions tab
|
|
||||||
workflow_dispatch:
|
|
||||||
inputs:
|
|
||||||
logLevel:
|
|
||||||
description: 'Log level'
|
|
||||||
required: true
|
|
||||||
default: 'warning'
|
|
||||||
tags:
|
|
||||||
description: 'Test scenario tags'
|
|
||||||
|
|
||||||
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
|
|
||||||
jobs:
|
|
||||||
buildx:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
-
|
|
||||||
name: Checkout
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
-
|
|
||||||
name: Get current date
|
|
||||||
id: date
|
|
||||||
run: echo "::set-output name=today::$(date +'%Y-%m-%d')"
|
|
||||||
-
|
|
||||||
name: Set up QEMU
|
|
||||||
uses: docker/setup-qemu-action@v1
|
|
||||||
with:
|
|
||||||
platforms: all
|
|
||||||
-
|
|
||||||
name: Set up Docker Buildx
|
|
||||||
id: buildx
|
|
||||||
uses: docker/setup-buildx-action@v1
|
|
||||||
-
|
|
||||||
name: Available platforms
|
|
||||||
run: echo ${{ steps.buildx.outputs.platforms }}
|
|
||||||
-
|
|
||||||
name: Cache Docker layers
|
|
||||||
uses: actions/cache@v2
|
|
||||||
with:
|
|
||||||
path: /tmp/.buildx-cache
|
|
||||||
key: ${{ runner.os }}-buildx-${{ github.sha }}
|
|
||||||
restore-keys: |
|
|
||||||
${{ runner.os }}-buildx-
|
|
||||||
-
|
|
||||||
name: Login to DockerHub
|
|
||||||
uses: docker/login-action@v1
|
|
||||||
with:
|
|
||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
|
||||||
-
|
|
||||||
name: Build and push
|
|
||||||
uses: docker/build-push-action@v2
|
|
||||||
with:
|
|
||||||
context: .
|
|
||||||
file: ./Dockerfile
|
|
||||||
platforms: linux/amd64,linux/arm64
|
|
||||||
push: ${{ github.event_name != 'pull_request' }}
|
|
||||||
cache-from: type=local,src=/tmp/.buildx-cache
|
|
||||||
cache-to: type=local,dest=/tmp/.buildx-cache
|
|
||||||
tags: |
|
|
||||||
zx5253/webdav-aliyundriver:latest
|
|
||||||
zx5253/webdav-aliyundriver:${{ steps.date.outputs.today }}
|
|
|
@ -7,5 +7,4 @@ RUN cd /tmp/code && mvn clean package -Dmaven.test.skip=true -Dmaven.javadoc.ski
|
||||||
FROM openjdk:11-jdk-oracle
|
FROM openjdk:11-jdk-oracle
|
||||||
COPY --from=maven /tmp/code/target/*.jar /webdav.jar
|
COPY --from=maven /tmp/code/target/*.jar /webdav.jar
|
||||||
EXPOSE 8080
|
EXPOSE 8080
|
||||||
ENV JAVA_OPTS="-Xmx1g"
|
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/webdav.jar"]
|
||||||
ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar /webdav.jar"]
|
|
||||||
|
|
122
README.md
122
README.md
|
@ -1,32 +1,7 @@
|
||||||
|
|
||||||
说明:[1.1.0版本](https://github.com/zxbu/webdav-aliyundriver/releases/tag/v1.1.0)支持阿里Teambition网盘的webdav协议,后续的2.x版本仅支持阿里云盘,不再维护Teambition网盘版本
|
说明:[1.1.0版本](https://github.com/zxbu/webdav-aliyundriver/releases/tag/v1.1.0)支持阿里Teambition网盘的webdav协议,后续的2.x版本仅支持阿里云盘,不再维护Teambition网盘版本
|
||||||
|
|
||||||
- [webdav-aliyundriver](#webdav-aliyundriver)
|
|
||||||
- [如何使用](#如何使用)
|
|
||||||
- [Jar包运行](#jar包运行)
|
|
||||||
- [容器运行](#容器运行)
|
|
||||||
- [Docker-Compose](#docker-compose)
|
|
||||||
- [参数说明](#参数说明)
|
|
||||||
- [QQ群](#qq群)
|
|
||||||
- [新手教程](#新手教程)
|
|
||||||
- [群晖](#群晖)
|
|
||||||
- [Windows10](#windows10)
|
|
||||||
- [Linux](#linux)
|
|
||||||
- [Mac](#mac)
|
|
||||||
- [客户端兼容性](#客户端兼容性)
|
|
||||||
- [浏览器获取refreshToken方式](#浏览器获取refreshtoken方式)
|
|
||||||
- [功能说明](#功能说明)
|
|
||||||
- [支持的功能](#支持的功能)
|
|
||||||
- [暂不支持的功能](#暂不支持的功能)
|
|
||||||
- [已知问题](#已知问题)
|
|
||||||
- [TODO](#todo)
|
|
||||||
- [免责声明](#免责声明)
|
|
||||||
|
|
||||||
# webdav-aliyundriver
|
# webdav-aliyundriver
|
||||||
本项目实现了阿里云盘的webdav协议,只需要简单的配置一下,就可以让阿里云盘变身为webdav协议的文件服务器。
|
本项目实现了阿里云盘的webdav协议,只需要简单的配置一下,就可以让阿里云盘变身为webdav协议的文件服务器。
|
||||||
基于此,你可以把阿里云盘挂载为Windows、Linux、Mac系统的磁盘,可以通过NAS系统做文件管理或文件同步,更多玩法等你挖掘
|
基于此,你可以把阿里云盘挂载为Windows、Linux、Mac系统的磁盘,可以通过NAS系统做文件管理或文件同步,更多玩法等你挖掘
|
||||||
|
|
||||||
|
|
||||||
# 如何使用
|
# 如何使用
|
||||||
支持refreshToken登录方式,具体看参数说明
|
支持refreshToken登录方式,具体看参数说明
|
||||||
## Jar包运行
|
## Jar包运行
|
||||||
|
@ -37,39 +12,11 @@ java -jar webdav.jar --aliyundrive.refresh-token="your refreshToken"
|
||||||
```
|
```
|
||||||
## 容器运行
|
## 容器运行
|
||||||
```bash
|
```bash
|
||||||
docker run -d --name=webdav-aliyundriver --restart=always -p 8080:8080 -v /etc/localtime:/etc/localtime -v /etc/aliyun-driver/:/etc/aliyun-driver/ -e TZ="Asia/Shanghai" -e ALIYUNDRIVE_REFRESH_TOKEN="your refreshToken" -e ALIYUNDRIVE_AUTH_PASSWORD="admin" -e JAVA_OPTS="-Xmx1g" zx5253/webdav-aliyundriver
|
docker run -d --name=webdav-aliyundriver --restart=always -p 8080:8080 -v /etc/localtime:/etc/localtime -v /etc/aliyun-driver/:/etc/aliyun-driver/ -e TZ="Asia/Shanghai" -e ALIYUNDRIVE_REFRESH_TOKEN="your refreshToken" zx5253/webdav-aliyundriver
|
||||||
|
|
||||||
# /etc/aliyun-driver/ 挂载卷自动维护了最新的refreshToken,建议挂载
|
# /etc/aliyun-driver/ 挂载卷自动维护了最新的refreshToken,建议挂载
|
||||||
# ALIYUNDRIVE_AUTH_PASSWORD 是admin账户的密码,建议修改
|
|
||||||
# JAVA_OPTS 可修改最大内存占用,比如 -e JAVA_OPTS="-Xmx512m" 表示最大内存限制为512m
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Docker-Compose
|
|
||||||
```yml
|
|
||||||
version: "3.0"
|
|
||||||
services:
|
|
||||||
webdav-aliyundriver:
|
|
||||||
image: zx5253/webdav-aliyundriver
|
|
||||||
container_name: aliyundriver
|
|
||||||
environment:
|
|
||||||
- TZ=Asia/Shanghai
|
|
||||||
- ALIYUNDRIVE_REFRESH_TOKEN=refreshToken
|
|
||||||
- ALIYUNDRIVE_AUTH_USER_NAME=admin
|
|
||||||
- ALIYUNDRIVE_AUTH_PASSWORD=admin
|
|
||||||
- JAVA_OPTS=-Xmx1g
|
|
||||||
volumes:
|
|
||||||
- /etc/aliyun-driver/:/etc/aliyun-driver/
|
|
||||||
ports:
|
|
||||||
- 6666:8080
|
|
||||||
restart: always
|
|
||||||
|
|
||||||
# “refreshToken”请根据下文说明自行获取。
|
|
||||||
# “ALIYUNDRIVE_AUTH_USER-NAME”和“ALIYUNDRIVE_AUTH_PASSWORD”为连接用户名和密码,建议更改。
|
|
||||||
# “/etc/aliyun-driver/:/etc/aliyun-driver/”,可以把冒号前改为指定目录,比如“/homes/USER/docker/alidriver/:/etc/aliyun-driver/”。
|
|
||||||
# 删除了“/etc/localtime:/etc/localtime”,如有需要同步时间请自行添加在environment下。
|
|
||||||
# 端口6666可自行按需更改,此端口为WebDAV连接端口,8080为容器内配置端口,修改请量力而为。
|
|
||||||
# 建议不要保留这些中文注释,以防报错,比如QNAP。
|
|
||||||
```
|
|
||||||
|
|
||||||
# 参数说明
|
# 参数说明
|
||||||
```bash
|
```bash
|
||||||
|
@ -77,49 +24,9 @@ services:
|
||||||
阿里云盘的refreshToken,获取方式见下文
|
阿里云盘的refreshToken,获取方式见下文
|
||||||
--server.port
|
--server.port
|
||||||
非必填,服务器端口号,默认为8080
|
非必填,服务器端口号,默认为8080
|
||||||
--aliyundrive.auth.enable=true
|
|
||||||
是否开启WebDav账户验证,默认开启
|
|
||||||
--aliyundrive.auth.user-name=admin
|
|
||||||
WebDav账户,默认admin
|
|
||||||
--aliyundrive.auth.password=admin
|
|
||||||
WebDav密码,默认admin
|
|
||||||
--aliyundrive.work-dir=/etc/aliyun-driver/
|
|
||||||
token挂载路径(如果多开的话,需修改此配置)
|
|
||||||
|
|
||||||
```
|
```
|
||||||
# QQ群
|
# QQ群
|
||||||
> 群号(已满):789738128
|
> 群号:789738128
|
||||||
|
|
||||||
> 二群群号(已满):979024890
|
|
||||||
|
|
||||||
> 三群群号(已满):212673498
|
|
||||||
|
|
||||||
> 四群群号(已满):752067171
|
|
||||||
|
|
||||||
> 五群群号:555954095
|
|
||||||
|
|
||||||
# 新手教程
|
|
||||||
## 群晖
|
|
||||||
TODO
|
|
||||||
|
|
||||||
## Windows10
|
|
||||||
TODO
|
|
||||||
|
|
||||||
## Linux
|
|
||||||
TODO
|
|
||||||
|
|
||||||
## Mac
|
|
||||||
TODO
|
|
||||||
|
|
||||||
# 客户端兼容性
|
|
||||||
| 客户端 | 下载 | 上传 | 备注 |
|
|
||||||
| :-----| ----: | :----: | :----: |
|
|
||||||
| 群辉Cloud Sync | 可用 | 可用 | 使用单向同步非常稳定 |
|
|
||||||
| Rclone | 可用 | 可用 | 推荐,支持各个系统 |
|
|
||||||
| Mac原生 | 可用 | 可用 | |
|
|
||||||
| Windows原生 | 可用 | 有点小问题 | 不建议,适配有点问题,上传报错 |
|
|
||||||
| RaiDrive | 可用 | 可用 | Windows平台下建议用这个 |
|
|
||||||
|
|
||||||
|
|
||||||
# 浏览器获取refreshToken方式
|
# 浏览器获取refreshToken方式
|
||||||
1. 先通过浏览器(建议chrome)打开阿里云盘官网并登录:https://www.aliyundrive.com/drive/
|
1. 先通过浏览器(建议chrome)打开阿里云盘官网并登录:https://www.aliyundrive.com/drive/
|
||||||
|
@ -135,27 +42,16 @@ TODO
|
||||||
4. 文件下载
|
4. 文件下载
|
||||||
5. 文件删除
|
5. 文件删除
|
||||||
6. 文件上传(支持大文件自动分批上传)
|
6. 文件上传(支持大文件自动分批上传)
|
||||||
7. 支持超大文件上传(官方限制30G)
|
|
||||||
8. 支持WebDav权限校验(默认账户密码:admin/admin)
|
|
||||||
9. 文件下载断点续传
|
|
||||||
10. Webdav下的流媒体播放等功能
|
|
||||||
## 暂不支持的功能
|
## 暂不支持的功能
|
||||||
1. 移动文件到其他目录的同时,修改文件名。比如 /a.zip 移动到 /b/a1.zip,是不支持的
|
1. 权限校验
|
||||||
2. 文件上传断点续传
|
2. 移动文件到其他目录的同时,修改文件名。比如 /a.zip 移动到 /b/a1.zip,是不支持的
|
||||||
3. 部分客户端兼容性不好
|
3. 文件上传断点续传
|
||||||
|
4. 文件下载断点续传
|
||||||
|
5. 同级目录下文件数量不能超过10000个(建议不超过100,否则性能比较差)
|
||||||
## 已知问题
|
## 已知问题
|
||||||
1. 没有做文件sha1校验,不保证上传文件的100%准确性(一般场景下,是没问题的)
|
1. 没有做文件sha1校验,不保证上传文件的100%准确性(一般场景下,是没问题的)
|
||||||
2. 通过文件名和文件大小判断是否重复。也就是说如果一个文件即使发生了更新,但其大小没有任何改变,是不会自动上传的
|
2. 通过文件名和文件大小判断是否重复。也就是说如果一个文件即使发生了更新,但其大小没有任何改变,是不会自动上传的
|
||||||
3. 不支持文件名包含 `/` 字符
|
3. 超大文件上传存在问题,具体上限还不清楚,我自己实测5G+大小的文件无法顺利上传
|
||||||
|
|
||||||
## TODO
|
## TODO
|
||||||
1. 支持更多登录方式(验证码、账号密码等)
|
1. 支持更多登录方式(验证码、账号密码等)
|
||||||
|
2. 支持权限校验
|
||||||
|
|
||||||
# 免责声明
|
|
||||||
1. 本软件为免费开源项目,无任何形式的盈利行为。
|
|
||||||
2. 本软件服务于阿里云盘,旨在让阿里云盘功能更强大。如有侵权,请与我联系,会及时处理。
|
|
||||||
3. 本软件皆调用官方接口实现,无任何“Hack”行为,无破坏官方接口行为。
|
|
||||||
5. 本软件仅做流量转发,不拦截、存储、篡改任何用户数据。
|
|
||||||
6. 严禁使用本软件进行盈利、损坏官方、散落任何违法信息等行为。
|
|
||||||
7. 本软件不作任何稳定性的承诺,如因使用本软件导致的文件丢失、文件破坏等意外情况,均与本软件无关。
|
|
||||||
|
|
|
@ -1,17 +0,0 @@
|
||||||
version: '3'
|
|
||||||
services:
|
|
||||||
# https://github.com/zxbu/webdav-aliyundriver
|
|
||||||
webdav-aliyundriver:
|
|
||||||
image: zx5253/webdav-aliyundriver
|
|
||||||
container_name: webdav-aliyundriver
|
|
||||||
restart: always
|
|
||||||
volumes:
|
|
||||||
- /etc/localtime:/etc/localtime
|
|
||||||
- ./docker/etc/aliyun-driver/:/etc/aliyun-driver/
|
|
||||||
ports:
|
|
||||||
- "8080:8080"
|
|
||||||
tty: true
|
|
||||||
environment:
|
|
||||||
- TZ=Asia/Shanghai
|
|
||||||
- ALIYUNDRIVE_REFRESH_TOKEN=<change me>
|
|
||||||
- ALIYUNDRIVE_AUTH_PASSWORD=<change me>
|
|
8
pom.xml
8
pom.xml
|
@ -10,7 +10,7 @@
|
||||||
</parent>
|
</parent>
|
||||||
<groupId>com.github.zxbu</groupId>
|
<groupId>com.github.zxbu</groupId>
|
||||||
<artifactId>webdav-aliyundriver</artifactId>
|
<artifactId>webdav-aliyundriver</artifactId>
|
||||||
<version>2.4.0</version>
|
<version>2.2.0</version>
|
||||||
<name>webdav</name>
|
<name>webdav</name>
|
||||||
<description>Demo project for Spring Boot</description>
|
<description>Demo project for Spring Boot</description>
|
||||||
|
|
||||||
|
@ -30,12 +30,6 @@
|
||||||
<version>3.14.9</version>
|
<version>3.14.9</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.github.ben-manes.caffeine</groupId>
|
|
||||||
<artifactId>caffeine</artifactId>
|
|
||||||
<version>2.7.0</version>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
|
|
@ -8,13 +8,11 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
import org.springframework.boot.web.servlet.FilterRegistrationBean;
|
import org.springframework.boot.web.servlet.FilterRegistrationBean;
|
||||||
import org.springframework.boot.web.servlet.ServletRegistrationBean;
|
import org.springframework.boot.web.servlet.ServletRegistrationBean;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
|
||||||
|
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
@SpringBootApplication
|
@SpringBootApplication
|
||||||
@EnableScheduling
|
|
||||||
public class WebdavTeambitionApplication {
|
public class WebdavTeambitionApplication {
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
|
|
|
@ -7,9 +7,7 @@ import okhttp3.*;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
import org.springframework.util.StringUtils;
|
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
@ -44,14 +42,7 @@ public class AliYunDriverClient {
|
||||||
@Override
|
@Override
|
||||||
public Request authenticate(Route route, Response response) throws IOException {
|
public Request authenticate(Route route, Response response) throws IOException {
|
||||||
if (response.code() == 401 && response.body() != null && response.body().string().contains("AccessToken")) {
|
if (response.code() == 401 && response.body() != null && response.body().string().contains("AccessToken")) {
|
||||||
String refreshTokenResult;
|
String refreshTokenResult = post("https://websv.aliyundrive.com/token/refresh", Collections.singletonMap("refresh_token", readRefreshToken()));
|
||||||
try {
|
|
||||||
refreshTokenResult = post("https://websv.aliyundrive.com/token/refresh", Collections.singletonMap("refresh_token", readRefreshToken()));
|
|
||||||
} catch (Exception e) {
|
|
||||||
// 如果置换token失败,先清空原token文件,再尝试一次
|
|
||||||
deleteRefreshTokenFile();
|
|
||||||
refreshTokenResult = post("https://websv.aliyundrive.com/token/refresh", Collections.singletonMap("refresh_token", readRefreshToken()));
|
|
||||||
}
|
|
||||||
String accessToken = (String) JsonUtil.getJsonNodeValue(refreshTokenResult, "access_token");
|
String accessToken = (String) JsonUtil.getJsonNodeValue(refreshTokenResult, "access_token");
|
||||||
String refreshToken = (String) JsonUtil.getJsonNodeValue(refreshTokenResult, "refresh_token");
|
String refreshToken = (String) JsonUtil.getJsonNodeValue(refreshTokenResult, "refresh_token");
|
||||||
Assert.hasLength(accessToken, "获取accessToken失败");
|
Assert.hasLength(accessToken, "获取accessToken失败");
|
||||||
|
@ -94,32 +85,12 @@ public class AliYunDriverClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public Response download(String url, HttpServletRequest httpServletRequest, long size ) {
|
public InputStream download(String url) {
|
||||||
Request.Builder builder = new Request.Builder().header("referer", "https://www.aliyundrive.com/");
|
Request request = new Request.Builder().url(url).build();
|
||||||
String range = httpServletRequest.getHeader("range");
|
|
||||||
if (range != null) {
|
|
||||||
// 如果range最后 >= size, 则去掉
|
|
||||||
String[] split = range.split("-");
|
|
||||||
if (split.length == 2) {
|
|
||||||
String end = split[1];
|
|
||||||
if (Long.parseLong(end) >= size) {
|
|
||||||
range = range.substring(0, range.lastIndexOf('-') + 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
builder.header("range", range);
|
|
||||||
}
|
|
||||||
|
|
||||||
String ifRange = httpServletRequest.getHeader("if-range");
|
|
||||||
if (ifRange != null) {
|
|
||||||
builder.header("if-range", ifRange);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Request request = builder.url(url).build();
|
|
||||||
Response response = null;
|
Response response = null;
|
||||||
try {
|
try {
|
||||||
response = okHttpClient.newCall(request).execute();
|
response = okHttpClient.newCall(request).execute();
|
||||||
return response;
|
return response.body().byteStream();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new WebdavException(e);
|
throw new WebdavException(e);
|
||||||
}
|
}
|
||||||
|
@ -207,16 +178,6 @@ public class AliYunDriverClient {
|
||||||
return aliYunDriveProperties.getUrl() + url;
|
return aliYunDriveProperties.getUrl() + url;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void deleteRefreshTokenFile() {
|
|
||||||
String refreshTokenPath = aliYunDriveProperties.getWorkDir() + "refresh-token";
|
|
||||||
Path path = Paths.get(refreshTokenPath);
|
|
||||||
try {
|
|
||||||
Files.delete(path);
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private String readRefreshToken() {
|
private String readRefreshToken() {
|
||||||
String refreshTokenPath = aliYunDriveProperties.getWorkDir() + "refresh-token";
|
String refreshTokenPath = aliYunDriveProperties.getWorkDir() + "refresh-token";
|
||||||
Path path = Paths.get(refreshTokenPath);
|
Path path = Paths.get(refreshTokenPath);
|
||||||
|
|
|
@ -1,28 +0,0 @@
|
||||||
package com.github.zxbu.webdavteambition.config;
|
|
||||||
|
|
||||||
import com.github.zxbu.webdavteambition.model.result.TFile;
|
|
||||||
import com.github.zxbu.webdavteambition.store.AliYunDriverClientService;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.scheduling.annotation.Scheduled;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
|
|
||||||
@Component
|
|
||||||
public class AliYunDriverCronTask {
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private AliYunDriverClientService aliYunDriverClientService;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 每隔5分钟请求一下接口,保证token不过期
|
|
||||||
*/
|
|
||||||
@Scheduled(initialDelay = 30 * 1000, fixedDelay = 5 * 60 * 1000)
|
|
||||||
public void refreshToken() {
|
|
||||||
try {
|
|
||||||
TFile root = aliYunDriverClientService.getTFileByPath("/");
|
|
||||||
aliYunDriverClientService.getTFiles(root.getFile_id());
|
|
||||||
} catch (Exception e) {
|
|
||||||
// nothing
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,8 +1,6 @@
|
||||||
package com.github.zxbu.webdavteambition.config;
|
package com.github.zxbu.webdavteambition.config;
|
||||||
|
|
||||||
import org.apache.catalina.CredentialHandler;
|
import org.apache.catalina.authenticator.DigestAuthenticator;
|
||||||
import org.apache.catalina.authenticator.AuthenticatorBase;
|
|
||||||
import org.apache.catalina.authenticator.BasicAuthenticator;
|
|
||||||
import org.apache.catalina.realm.GenericPrincipal;
|
import org.apache.catalina.realm.GenericPrincipal;
|
||||||
import org.apache.catalina.realm.MessageDigestCredentialHandler;
|
import org.apache.catalina.realm.MessageDigestCredentialHandler;
|
||||||
import org.apache.catalina.realm.RealmBase;
|
import org.apache.catalina.realm.RealmBase;
|
||||||
|
@ -21,7 +19,7 @@ import java.util.Collections;
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
@ConditionalOnProperty(prefix = "aliyundrive.auth", name = "enable", matchIfMissing = true)
|
@ConditionalOnProperty(prefix = "aliyundrive.auth", name = "enable", matchIfMissing = true)
|
||||||
public class AuthTomcatConfig implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory>, Ordered {
|
public class EmbeddedTomcatConfig implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory>, Ordered {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private AliYunDriveProperties aliYunDriveProperties;
|
private AliYunDriveProperties aliYunDriveProperties;
|
||||||
|
@ -39,7 +37,7 @@ public class AuthTomcatConfig implements WebServerFactoryCustomizer<Configurable
|
||||||
if (aliYunDriveProperties.getAuth().getUserName().equals(username)) {
|
if (aliYunDriveProperties.getAuth().getUserName().equals(username)) {
|
||||||
return aliYunDriveProperties.getAuth().getPassword();
|
return aliYunDriveProperties.getAuth().getPassword();
|
||||||
}
|
}
|
||||||
return null;
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -48,11 +46,11 @@ public class AuthTomcatConfig implements WebServerFactoryCustomizer<Configurable
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
CredentialHandler credentialHandler = new MessageDigestCredentialHandler();
|
MessageDigestCredentialHandler credentialHandler = new MessageDigestCredentialHandler();
|
||||||
realm.setCredentialHandler(credentialHandler);
|
realm.setCredentialHandler(credentialHandler);
|
||||||
context.setRealm(realm);
|
context.setRealm(realm);
|
||||||
|
|
||||||
AuthenticatorBase digestAuthenticator = new BasicAuthenticator();
|
DigestAuthenticator digestAuthenticator = new DigestAuthenticator();
|
||||||
SecurityConstraint securityConstraint = new SecurityConstraint();
|
SecurityConstraint securityConstraint = new SecurityConstraint();
|
||||||
securityConstraint.setAuthConstraint(true);
|
securityConstraint.setAuthConstraint(true);
|
||||||
securityConstraint.addAuthRole("**");
|
securityConstraint.addAuthRole("**");
|
|
@ -1,31 +0,0 @@
|
||||||
package com.github.zxbu.webdavteambition.model;
|
|
||||||
|
|
||||||
public class DownloadRequest {
|
|
||||||
private String drive_id;
|
|
||||||
private String file_id;
|
|
||||||
private Integer expire_sec = 14400;
|
|
||||||
|
|
||||||
public String getDrive_id() {
|
|
||||||
return drive_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setDrive_id(String drive_id) {
|
|
||||||
this.drive_id = drive_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getFile_id() {
|
|
||||||
return file_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setFile_id(String file_id) {
|
|
||||||
this.file_id = file_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Integer getExpire_sec() {
|
|
||||||
return expire_sec;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setExpire_sec(Integer expire_sec) {
|
|
||||||
this.expire_sec = expire_sec;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,17 +1,17 @@
|
||||||
package com.github.zxbu.webdavteambition.model;
|
package com.github.zxbu.webdavteambition.model;
|
||||||
|
|
||||||
public class Page {
|
public class Page {
|
||||||
private String marker;
|
private int offset;
|
||||||
private int limit;
|
private int limit;
|
||||||
private String order_by;
|
private String order_by;
|
||||||
private String order_direction;
|
private String order_direction;
|
||||||
|
|
||||||
public String getMarker() {
|
public int getOffset() {
|
||||||
return marker;
|
return offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setMarker(String marker) {
|
public void setOffset(int offset) {
|
||||||
this.marker = marker;
|
this.offset = offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getLimit() {
|
public int getLimit() {
|
||||||
|
|
|
@ -1,42 +0,0 @@
|
||||||
package com.github.zxbu.webdavteambition.model;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class RefreshUploadUrlRequest {
|
|
||||||
private String drive_id;
|
|
||||||
private List<UploadPreRequest.PartInfo> part_info_list;
|
|
||||||
private String file_id;
|
|
||||||
private String upload_id;
|
|
||||||
|
|
||||||
public String getDrive_id() {
|
|
||||||
return drive_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setDrive_id(String drive_id) {
|
|
||||||
this.drive_id = drive_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<UploadPreRequest.PartInfo> getPart_info_list() {
|
|
||||||
return part_info_list;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setPart_info_list(List<UploadPreRequest.PartInfo> part_info_list) {
|
|
||||||
this.part_info_list = part_info_list;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getFile_id() {
|
|
||||||
return file_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setFile_id(String file_id) {
|
|
||||||
this.file_id = file_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getUpload_id() {
|
|
||||||
return upload_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setUpload_id(String upload_id) {
|
|
||||||
this.upload_id = upload_id;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -4,7 +4,6 @@ import java.util.List;
|
||||||
|
|
||||||
public class TFileListResult<T> {
|
public class TFileListResult<T> {
|
||||||
private List<T> items;
|
private List<T> items;
|
||||||
private String next_marker;
|
|
||||||
|
|
||||||
public List<T> getItems() {
|
public List<T> getItems() {
|
||||||
return items;
|
return items;
|
||||||
|
@ -13,12 +12,4 @@ public class TFileListResult<T> {
|
||||||
public void setItems(List<T> items) {
|
public void setItems(List<T> items) {
|
||||||
this.items = items;
|
this.items = items;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getNext_marker() {
|
|
||||||
return next_marker;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setNext_marker(String next_marker) {
|
|
||||||
this.next_marker = next_marker;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,8 +2,6 @@ package com.github.zxbu.webdavteambition.store;
|
||||||
|
|
||||||
import com.fasterxml.jackson.core.type.TypeReference;
|
import com.fasterxml.jackson.core.type.TypeReference;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import com.github.benmanes.caffeine.cache.Cache;
|
|
||||||
import com.github.benmanes.caffeine.cache.Caffeine;
|
|
||||||
import com.github.zxbu.webdavteambition.client.AliYunDriverClient;
|
import com.github.zxbu.webdavteambition.client.AliYunDriverClient;
|
||||||
import com.github.zxbu.webdavteambition.model.*;
|
import com.github.zxbu.webdavteambition.model.*;
|
||||||
import com.github.zxbu.webdavteambition.model.result.TFile;
|
import com.github.zxbu.webdavteambition.model.result.TFile;
|
||||||
|
@ -11,21 +9,16 @@ import com.github.zxbu.webdavteambition.model.result.TFileListResult;
|
||||||
import com.github.zxbu.webdavteambition.model.result.UploadPreResult;
|
import com.github.zxbu.webdavteambition.model.result.UploadPreResult;
|
||||||
import com.github.zxbu.webdavteambition.util.JsonUtil;
|
import com.github.zxbu.webdavteambition.util.JsonUtil;
|
||||||
import net.sf.webdav.exceptions.WebdavException;
|
import net.sf.webdav.exceptions.WebdavException;
|
||||||
import okhttp3.HttpUrl;
|
|
||||||
import okhttp3.Response;
|
|
||||||
import org.apache.tomcat.util.http.fileupload.IOUtils;
|
import org.apache.tomcat.util.http.fileupload.IOUtils;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
public class AliYunDriverClientService {
|
public class AliYunDriverClientService {
|
||||||
|
@ -34,37 +27,33 @@ public class AliYunDriverClientService {
|
||||||
private static String rootPath = "/";
|
private static String rootPath = "/";
|
||||||
private static int chunkSize = 10485760; // 10MB
|
private static int chunkSize = 10485760; // 10MB
|
||||||
private TFile rootTFile = null;
|
private TFile rootTFile = null;
|
||||||
|
private ThreadLocal<Map<String, Set<TFile>>> tFilesCache = new ThreadLocal<>();
|
||||||
private static Cache<String, Set<TFile>> tFilesCache = Caffeine.newBuilder()
|
|
||||||
.initialCapacity(128)
|
|
||||||
.maximumSize(1024)
|
|
||||||
.expireAfterWrite(1, TimeUnit.MINUTES)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
private final AliYunDriverClient client;
|
private final AliYunDriverClient client;
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private VirtualTFileService virtualTFileService;
|
|
||||||
|
|
||||||
public AliYunDriverClientService(AliYunDriverClient aliYunDriverClient) {
|
public AliYunDriverClientService(AliYunDriverClient aliYunDriverClient) {
|
||||||
this.client = aliYunDriverClient;
|
this.client = aliYunDriverClient;
|
||||||
AliYunDriverFileSystemStore.setBean(this);
|
AliYunDriverFileSystemStore.setBean(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<TFile> getTFiles(String nodeId) {
|
public Set<TFile> getTFiles(String nodeId) {
|
||||||
Set<TFile> tFiles = tFilesCache.get(nodeId, key -> {
|
Map<String, Set<TFile>> map = tFilesCache.get();
|
||||||
// 获取真实的文件列表
|
if (map == null) {
|
||||||
return getTFiles2(nodeId);
|
map = new ConcurrentHashMap<>();
|
||||||
});
|
tFilesCache.set(map);
|
||||||
Set<TFile> all = new LinkedHashSet<>(tFiles);
|
|
||||||
// 获取上传中的文件列表
|
|
||||||
Collection<TFile> virtualTFiles = virtualTFileService.list(nodeId);
|
|
||||||
all.addAll(virtualTFiles);
|
|
||||||
return all;
|
|
||||||
}
|
}
|
||||||
|
return map.computeIfAbsent(nodeId, key -> {
|
||||||
private Set<TFile> getTFiles2(String nodeId) {
|
FileListRequest listQuery = new FileListRequest();
|
||||||
List<TFile> tFileList = fileListFromApi(nodeId, null, new ArrayList<>());
|
listQuery.setOffset(0);
|
||||||
|
listQuery.setLimit(10000);
|
||||||
|
listQuery.setOrder_by("updated_at");
|
||||||
|
listQuery.setOrder_direction("DESC");
|
||||||
|
listQuery.setDrive_id(client.getDriveId());
|
||||||
|
listQuery.setParent_file_id(nodeId);
|
||||||
|
String json = client.post("/file/list", listQuery);
|
||||||
|
TFileListResult<TFile> tFileListResult = JsonUtil.readValue(json, new TypeReference<TFileListResult<TFile>>() {
|
||||||
|
});
|
||||||
|
List<TFile> tFileList = tFileListResult.getItems();
|
||||||
tFileList.sort(Comparator.comparing(TFile::getUpdated_at).reversed());
|
tFileList.sort(Comparator.comparing(TFile::getUpdated_at).reversed());
|
||||||
Set<TFile> tFileSets = new LinkedHashSet<>();
|
Set<TFile> tFileSets = new LinkedHashSet<>();
|
||||||
for (TFile tFile : tFileList) {
|
for (TFile tFile : tFileList) {
|
||||||
|
@ -74,24 +63,8 @@ public class AliYunDriverClientService {
|
||||||
}
|
}
|
||||||
// 对文件名进行去重,只保留最新的一个
|
// 对文件名进行去重,只保留最新的一个
|
||||||
return tFileSets;
|
return tFileSets;
|
||||||
}
|
|
||||||
|
|
||||||
private List<TFile> fileListFromApi(String nodeId, String marker, List<TFile> all) {
|
|
||||||
FileListRequest listQuery = new FileListRequest();
|
|
||||||
listQuery.setMarker(marker);
|
|
||||||
listQuery.setLimit(100);
|
|
||||||
listQuery.setOrder_by("updated_at");
|
|
||||||
listQuery.setOrder_direction("DESC");
|
|
||||||
listQuery.setDrive_id(client.getDriveId());
|
|
||||||
listQuery.setParent_file_id(nodeId);
|
|
||||||
String json = client.post("/file/list", listQuery);
|
|
||||||
TFileListResult<TFile> tFileListResult = JsonUtil.readValue(json, new TypeReference<TFileListResult<TFile>>() {
|
|
||||||
});
|
});
|
||||||
all.addAll(tFileListResult.getItems());
|
|
||||||
if (!StringUtils.hasLength(tFileListResult.getNext_marker())) {
|
|
||||||
return all;
|
|
||||||
}
|
|
||||||
return fileListFromApi(nodeId, tFileListResult.getNext_marker(), all);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -152,43 +125,20 @@ public class AliYunDriverClientService {
|
||||||
UploadPreResult uploadPreResult = JsonUtil.readValue(json, UploadPreResult.class);
|
UploadPreResult uploadPreResult = JsonUtil.readValue(json, UploadPreResult.class);
|
||||||
List<UploadPreRequest.PartInfo> partInfoList = uploadPreResult.getPart_info_list();
|
List<UploadPreRequest.PartInfo> partInfoList = uploadPreResult.getPart_info_list();
|
||||||
if (partInfoList != null) {
|
if (partInfoList != null) {
|
||||||
if (size > 0) {
|
|
||||||
virtualTFileService.createTFile(parent.getFile_id(), uploadPreResult);
|
|
||||||
}
|
|
||||||
LOGGER.info("文件预处理成功,开始上传。文件名:{},上传URL数量:{}", path, partInfoList.size());
|
LOGGER.info("文件预处理成功,开始上传。文件名:{},上传URL数量:{}", path, partInfoList.size());
|
||||||
|
|
||||||
byte[] buffer = new byte[chunkSize];
|
byte[] buffer = new byte[chunkSize];
|
||||||
for (int i = 0; i < partInfoList.size(); i++) {
|
for (int i = 0; i < partInfoList.size(); i++) {
|
||||||
UploadPreRequest.PartInfo partInfo = partInfoList.get(i);
|
UploadPreRequest.PartInfo partInfo = partInfoList.get(i);
|
||||||
|
|
||||||
long expires = Long.parseLong(Objects.requireNonNull(Objects.requireNonNull(HttpUrl.parse(partInfo.getUpload_url())).queryParameter("x-oss-expires")));
|
|
||||||
if (System.currentTimeMillis() / 1000 + 10 >= expires) {
|
|
||||||
// 已过期,重新置换UploadUrl
|
|
||||||
RefreshUploadUrlRequest refreshUploadUrlRequest = new RefreshUploadUrlRequest();
|
|
||||||
refreshUploadUrlRequest.setDrive_id(client.getDriveId());
|
|
||||||
refreshUploadUrlRequest.setUpload_id(uploadPreResult.getUpload_id());
|
|
||||||
refreshUploadUrlRequest.setFile_id(uploadPreResult.getFile_id());
|
|
||||||
refreshUploadUrlRequest.setPart_info_list(part_info_list);
|
|
||||||
String refreshJson = client.post("/file/get_upload_url", refreshUploadUrlRequest);
|
|
||||||
UploadPreResult refreshResult = JsonUtil.readValue(refreshJson, UploadPreResult.class);
|
|
||||||
for (int j = i; j < partInfoList.size(); j++) {
|
|
||||||
UploadPreRequest.PartInfo oldInfo = partInfoList.get(j);
|
|
||||||
UploadPreRequest.PartInfo newInfo = refreshResult.getPart_info_list().stream().filter(p -> p.getPart_number().equals(oldInfo.getPart_number())).findAny().orElseThrow(NullPointerException::new);
|
|
||||||
oldInfo.setUpload_url(newInfo.getUpload_url());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
int read = IOUtils.read(inputStream, buffer, 0, buffer.length);
|
int read = IOUtils.read(inputStream, buffer, 0, buffer.length);
|
||||||
if (read == -1) {
|
if (read == -1) {
|
||||||
LOGGER.info("文件上传结束。文件名:{},当前进度:{}/{}", path, (i + 1), partInfoList.size());
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
client.upload(partInfo.getUpload_url(), buffer, 0, read);
|
client.upload(partInfo.getUpload_url(), buffer, 0, read);
|
||||||
virtualTFileService.updateLength(parent.getFile_id(), uploadPreResult.getFile_id(), buffer.length);
|
LOGGER.info("文件正在上传。文件名:{},当前进度:{}/{}", path, (i+1), partInfoList.size());
|
||||||
LOGGER.info("文件正在上传。文件名:{},当前进度:{}/{}", path, (i + 1), partInfoList.size());
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
virtualTFileService.remove(parent.getFile_id(), uploadPreResult.getFile_id());
|
|
||||||
throw new WebdavException(e);
|
throw new WebdavException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -202,12 +152,29 @@ public class AliYunDriverClientService {
|
||||||
uploadFinalRequest.setUpload_id(uploadPreResult.getUpload_id());
|
uploadFinalRequest.setUpload_id(uploadPreResult.getUpload_id());
|
||||||
|
|
||||||
client.post("/file/complete", uploadFinalRequest);
|
client.post("/file/complete", uploadFinalRequest);
|
||||||
virtualTFileService.remove(parent.getFile_id(), uploadPreResult.getFile_id());
|
|
||||||
LOGGER.info("文件上传成功。文件名:{}", path);
|
LOGGER.info("文件上传成功。文件名:{}", path);
|
||||||
|
// if (!uploadPreResult.getName().equals(pathInfo.getName())) {
|
||||||
|
// LOGGER.info("上传文件名{}与原文件名{}不同,对文件进行重命名", uploadPreResult.getName(), pathInfo.getName());
|
||||||
|
// TFile oldFile = getNodeIdByPath2(path);
|
||||||
|
// // 如果旧文件存在,则先删除
|
||||||
|
// if (oldFile != null) {
|
||||||
|
// LOGGER.info("旧文件{}还存在,大小为{},进行删除操作,可前往网页版的回收站查看", path, oldFile.getSize());
|
||||||
|
// remove(path);
|
||||||
|
// try {
|
||||||
|
// Thread.sleep(1500);
|
||||||
|
// } catch (InterruptedException e) {
|
||||||
|
// // no
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// RenameRequest renameRequest = new RenameRequest();
|
||||||
|
// renameRequest.setDrive_id(client.getDriveId());
|
||||||
|
// renameRequest.setFile_id(oldFile.getFile_id());
|
||||||
|
// renameRequest.setName(pathInfo.getName());
|
||||||
|
// client.post("/file/update", renameRequest);
|
||||||
|
// }
|
||||||
clearCache();
|
clearCache();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void rename(String sourcePath, String newName) {
|
public void rename(String sourcePath, String newName) {
|
||||||
sourcePath = normalizingPath(sourcePath);
|
sourcePath = normalizingPath(sourcePath);
|
||||||
TFile tFile = getTFileByPath(sourcePath);
|
TFile tFile = getTFileByPath(sourcePath);
|
||||||
|
@ -281,15 +248,9 @@ public class AliYunDriverClientService {
|
||||||
return getNodeIdByPath2(path);
|
return getNodeIdByPath2(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Response download(String path, HttpServletRequest request, long size ) {
|
public InputStream download(String path) {
|
||||||
TFile file = getTFileByPath(path);
|
// String downloadUrl = getTFileByPath(path).getDownloadUrl();
|
||||||
DownloadRequest downloadRequest = new DownloadRequest();
|
return client.download("downloadUrl");
|
||||||
downloadRequest.setDrive_id(client.getDriveId());
|
|
||||||
downloadRequest.setFile_id(file.getFile_id());
|
|
||||||
String json = client.post("/file/get_download_url", downloadRequest);
|
|
||||||
Object url = JsonUtil.getJsonNodeValue(json, "url");
|
|
||||||
LOGGER.debug("{} url = {}", path, url);
|
|
||||||
return client.download(url.toString(), request, size);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private TFile getNodeIdByPath2(String path) {
|
private TFile getNodeIdByPath2(String path) {
|
||||||
|
@ -363,7 +324,11 @@ public class AliYunDriverClientService {
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void clearCache() {
|
public void clearCache() {
|
||||||
tFilesCache.invalidateAll();
|
Map<String, Set<TFile>> map = tFilesCache.get();
|
||||||
|
if (map != null) {
|
||||||
|
map.clear();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,12 +3,9 @@ package com.github.zxbu.webdavteambition.store;
|
||||||
import com.github.zxbu.webdavteambition.model.FileType;
|
import com.github.zxbu.webdavteambition.model.FileType;
|
||||||
import com.github.zxbu.webdavteambition.model.PathInfo;
|
import com.github.zxbu.webdavteambition.model.PathInfo;
|
||||||
import com.github.zxbu.webdavteambition.model.result.TFile;
|
import com.github.zxbu.webdavteambition.model.result.TFile;
|
||||||
import net.sf.webdav.ITransaction;
|
import net.sf.webdav.*;
|
||||||
import net.sf.webdav.IWebdavStore;
|
import net.sf.webdav.exceptions.UnauthenticatedException;
|
||||||
import net.sf.webdav.StoredObject;
|
|
||||||
import net.sf.webdav.Transaction;
|
|
||||||
import net.sf.webdav.exceptions.WebdavException;
|
import net.sf.webdav.exceptions.WebdavException;
|
||||||
import okhttp3.Response;
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
@ -19,7 +16,6 @@ import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.security.Principal;
|
import java.security.Principal;
|
||||||
import java.util.Enumeration;
|
import java.util.Enumeration;
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
public class AliYunDriverFileSystemStore implements IWebdavStore {
|
public class AliYunDriverFileSystemStore implements IWebdavStore {
|
||||||
|
@ -39,23 +35,28 @@ public class AliYunDriverFileSystemStore implements IWebdavStore {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void destroy() {
|
public void destroy() {
|
||||||
LOGGER.info("destroy");
|
LOGGER.debug("destroy");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ITransaction begin(Principal principal, HttpServletRequest req, HttpServletResponse resp) {
|
public ITransaction begin(Principal principal, HttpServletRequest req, HttpServletResponse resp) {
|
||||||
LOGGER.debug("begin");
|
LOGGER.debug("begin");
|
||||||
|
aliYunDriverClientService.clearCache();
|
||||||
return new Transaction(principal, req, resp);
|
return new Transaction(principal, req, resp);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void checkAuthentication(ITransaction transaction) {
|
public void checkAuthentication(ITransaction transaction) {
|
||||||
LOGGER.debug("checkAuthentication");
|
LOGGER.debug("checkAuthentication");
|
||||||
|
if (transaction.getPrincipal() == null) {
|
||||||
|
throw new UnauthenticatedException(WebdavStatus.SC_UNAUTHORIZED);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void commit(ITransaction transaction) {
|
public void commit(ITransaction transaction) {
|
||||||
|
aliYunDriverClientService.clearCache();
|
||||||
LOGGER.debug("commit");
|
LOGGER.debug("commit");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,24 +81,8 @@ public class AliYunDriverFileSystemStore implements IWebdavStore {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public InputStream getResourceContent(ITransaction transaction, String resourceUri) {
|
public InputStream getResourceContent(ITransaction transaction, String resourceUri) {
|
||||||
LOGGER.info("getResourceContent: {}", resourceUri);
|
LOGGER.debug("getResourceContent: {}", resourceUri);
|
||||||
Enumeration<String> headerNames = transaction.getRequest().getHeaderNames();
|
return aliYunDriverClientService.download(resourceUri);
|
||||||
while (headerNames.hasMoreElements()) {
|
|
||||||
String s = headerNames.nextElement();
|
|
||||||
LOGGER.debug("{} request: {} = {}",resourceUri, s, transaction.getRequest().getHeader(s));
|
|
||||||
}
|
|
||||||
HttpServletResponse response = transaction.getResponse();
|
|
||||||
long size = getResourceLength(transaction, resourceUri);
|
|
||||||
Response downResponse = aliYunDriverClientService.download(resourceUri, transaction.getRequest(), size);
|
|
||||||
response.setContentLengthLong(downResponse.body().contentLength());
|
|
||||||
LOGGER.debug("{} code = {}", resourceUri, downResponse.code());
|
|
||||||
for (String name : downResponse.headers().names()) {
|
|
||||||
LOGGER.debug("{} downResponse: {} = {}", resourceUri, name, downResponse.header(name));
|
|
||||||
response.addHeader(name, downResponse.header(name));
|
|
||||||
}
|
|
||||||
response.setStatus(downResponse.code());
|
|
||||||
return downResponse.body().byteStream();
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -108,8 +93,7 @@ public class AliYunDriverFileSystemStore implements IWebdavStore {
|
||||||
|
|
||||||
long contentLength = request.getContentLength();
|
long contentLength = request.getContentLength();
|
||||||
if (contentLength < 0) {
|
if (contentLength < 0) {
|
||||||
contentLength = Long.parseLong(Optional.ofNullable(request.getHeader("content-length"))
|
contentLength = Long.parseLong(request.getHeader("content-length"));
|
||||||
.orElse(request.getHeader("X-Expected-Entity-Length")));
|
|
||||||
}
|
}
|
||||||
aliYunDriverClientService.uploadPre(resourceUri, contentLength, content);
|
aliYunDriverClientService.uploadPre(resourceUri, contentLength, content);
|
||||||
|
|
||||||
|
@ -131,7 +115,7 @@ public class AliYunDriverFileSystemStore implements IWebdavStore {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String[] getChildrenNames(ITransaction transaction, String folderUri) {
|
public String[] getChildrenNames(ITransaction transaction, String folderUri) {
|
||||||
LOGGER.info("getChildrenNames: {}", folderUri);
|
LOGGER.debug("getChildrenNames: {}", folderUri);
|
||||||
TFile tFile = aliYunDriverClientService.getTFileByPath(folderUri);
|
TFile tFile = aliYunDriverClientService.getTFileByPath(folderUri);
|
||||||
if (tFile.getType().equals(FileType.file.name())) {
|
if (tFile.getType().equals(FileType.file.name())) {
|
||||||
return new String[0];
|
return new String[0];
|
||||||
|
@ -144,11 +128,7 @@ public class AliYunDriverFileSystemStore implements IWebdavStore {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long getResourceLength(ITransaction transaction, String path) {
|
public long getResourceLength(ITransaction transaction, String path) {
|
||||||
return getResourceLength2(transaction, path);
|
LOGGER.debug("getResourceLength: {}", path);
|
||||||
}
|
|
||||||
|
|
||||||
public long getResourceLength2(ITransaction transaction, String path) {
|
|
||||||
LOGGER.info("getResourceLength: {}", path);
|
|
||||||
TFile tFile = aliYunDriverClientService.getTFileByPath(path);
|
TFile tFile = aliYunDriverClientService.getTFileByPath(path);
|
||||||
if (tFile == null || tFile.getSize() == null) {
|
if (tFile == null || tFile.getSize() == null) {
|
||||||
return 384;
|
return 384;
|
||||||
|
@ -186,12 +166,12 @@ public class AliYunDriverFileSystemStore implements IWebdavStore {
|
||||||
public StoredObject getStoredObject(ITransaction transaction, String uri) {
|
public StoredObject getStoredObject(ITransaction transaction, String uri) {
|
||||||
|
|
||||||
|
|
||||||
LOGGER.info("getStoredObject: {}", uri);
|
LOGGER.debug("getStoredObject: {}", uri);
|
||||||
TFile tFile = aliYunDriverClientService.getTFileByPath(uri);
|
TFile tFile = aliYunDriverClientService.getTFileByPath(uri);
|
||||||
if (tFile != null) {
|
if (tFile != null) {
|
||||||
StoredObject so = new StoredObject();
|
StoredObject so = new StoredObject();
|
||||||
so.setFolder(tFile.getType().equalsIgnoreCase("folder"));
|
so.setFolder(tFile.getType().equalsIgnoreCase("folder"));
|
||||||
so.setResourceLength(getResourceLength2(transaction, uri));
|
so.setResourceLength(getResourceLength(transaction, uri));
|
||||||
so.setCreationDate(tFile.getCreated_at());
|
so.setCreationDate(tFile.getCreated_at());
|
||||||
so.setLastModified(tFile.getUpdated_at());
|
so.setLastModified(tFile.getUpdated_at());
|
||||||
return so;
|
return so;
|
||||||
|
|
|
@ -1,68 +0,0 @@
|
||||||
package com.github.zxbu.webdavteambition.store;
|
|
||||||
|
|
||||||
import com.github.zxbu.webdavteambition.model.FileType;
|
|
||||||
import com.github.zxbu.webdavteambition.model.result.TFile;
|
|
||||||
import com.github.zxbu.webdavteambition.model.result.UploadPreResult;
|
|
||||||
import org.springframework.stereotype.Service;
|
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 虚拟文件(用于上传时,列表展示)
|
|
||||||
*/
|
|
||||||
@Service
|
|
||||||
public class VirtualTFileService {
|
|
||||||
private final Map<String, Map<String, TFile>> virtualTFileMap = new ConcurrentHashMap<>();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 创建文件
|
|
||||||
*/
|
|
||||||
public void createTFile(String parentId, UploadPreResult uploadPreResult) {
|
|
||||||
Map<String, TFile> tFileMap = virtualTFileMap.computeIfAbsent(parentId, s -> new ConcurrentHashMap<>());
|
|
||||||
tFileMap.put(uploadPreResult.getFile_id(), convert(uploadPreResult));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void updateLength(String parentId, String fileId, long length) {
|
|
||||||
Map<String, TFile> tFileMap = virtualTFileMap.get(parentId);
|
|
||||||
if (tFileMap == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
TFile tFile = tFileMap.get(fileId);
|
|
||||||
if (tFile == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
tFile.setSize(tFile.getSize() + length);
|
|
||||||
tFile.setUpdated_at(new Date());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void remove(String parentId, String fileId) {
|
|
||||||
Map<String, TFile> tFileMap = virtualTFileMap.get(parentId);
|
|
||||||
if (tFileMap == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
tFileMap.remove(fileId);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Collection<TFile> list(String parentId) {
|
|
||||||
Map<String, TFile> tFileMap = virtualTFileMap.get(parentId);
|
|
||||||
if (tFileMap == null) {
|
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
|
||||||
return tFileMap.values();
|
|
||||||
}
|
|
||||||
|
|
||||||
private TFile convert(UploadPreResult uploadPreResult) {
|
|
||||||
TFile tFile = new TFile();
|
|
||||||
tFile.setCreated_at(new Date());
|
|
||||||
tFile.setFile_id(uploadPreResult.getFile_id());
|
|
||||||
tFile.setName(uploadPreResult.getFile_name());
|
|
||||||
tFile.setType(FileType.file.name());
|
|
||||||
tFile.setUpdated_at(new Date());
|
|
||||||
tFile.setSize(0L);
|
|
||||||
return tFile;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -18,7 +18,6 @@ package net.sf.webdav.methods;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
|
||||||
import java.text.DateFormat;
|
import java.text.DateFormat;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
@ -33,8 +32,6 @@ import net.sf.webdav.IWebdavStore;
|
||||||
import net.sf.webdav.StoredObject;
|
import net.sf.webdav.StoredObject;
|
||||||
import net.sf.webdav.WebdavStatus;
|
import net.sf.webdav.WebdavStatus;
|
||||||
import net.sf.webdav.locking.ResourceLocks;
|
import net.sf.webdav.locking.ResourceLocks;
|
||||||
import org.apache.tomcat.util.http.fileupload.IOUtils;
|
|
||||||
import org.springframework.web.util.UriUtils;
|
|
||||||
|
|
||||||
public class DoGet extends DoHead {
|
public class DoGet extends DoHead {
|
||||||
|
|
||||||
|
@ -64,33 +61,30 @@ public class DoGet extends DoHead {
|
||||||
OutputStream out = resp.getOutputStream();
|
OutputStream out = resp.getOutputStream();
|
||||||
InputStream in = _store.getResourceContent(transaction, path);
|
InputStream in = _store.getResourceContent(transaction, path);
|
||||||
try {
|
try {
|
||||||
if (in != null) {
|
int read = -1;
|
||||||
LOG.debug("开始 {}, ", path);
|
byte[] copyBuffer = new byte[BUF_SIZE];
|
||||||
IOUtils.copyLarge(in, out);
|
|
||||||
LOG.debug("结束 {}", path);
|
while ((read = in.read(copyBuffer, 0, copyBuffer.length)) != -1) {
|
||||||
|
out.write(copyBuffer, 0, read);
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
// flushing causes a IOE if a file is opened on the webserver
|
// flushing causes a IOE if a file is opened on the webserver
|
||||||
// client disconnected before server finished sending response
|
// client disconnected before server finished sending response
|
||||||
try {
|
try {
|
||||||
if (in != null) {
|
|
||||||
in.close();
|
in.close();
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
LOG.warn("{} Closing InputStream causes Exception!\n", path
|
LOG.warn("Closing InputStream causes Exception!\n"
|
||||||
,e);
|
+ e.toString());
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
out.flush();
|
out.flush();
|
||||||
out.close();
|
out.close();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
LOG.warn("{} Flushing OutputStream causes Exception!\n", path
|
LOG.warn("Flushing OutputStream causes Exception!\n"
|
||||||
,e);
|
+ e.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
LOG.warn("{} doBody causes Exception!\n", path
|
|
||||||
,e);
|
|
||||||
LOG.trace(e.toString());
|
LOG.trace(e.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -147,7 +141,7 @@ public class DoGet extends DoHead {
|
||||||
childrenTemp.append("\">");
|
childrenTemp.append("\">");
|
||||||
childrenTemp.append("<td>");
|
childrenTemp.append("<td>");
|
||||||
childrenTemp.append("<a href=\"");
|
childrenTemp.append("<a href=\"");
|
||||||
childrenTemp.append(UriUtils.encode(child, "utf-8"));
|
childrenTemp.append(child);
|
||||||
StoredObject obj= _store.getStoredObject(transaction, path+"/"+child);
|
StoredObject obj= _store.getStoredObject(transaction, path+"/"+child);
|
||||||
if (obj == null)
|
if (obj == null)
|
||||||
{
|
{
|
||||||
|
|
|
@ -209,9 +209,6 @@ public class DoLock extends AbstractMethod {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
nullSo = _store.getStoredObject(transaction, _path);
|
nullSo = _store.getStoredObject(transaction, _path);
|
||||||
if (nullSo == null) {
|
|
||||||
nullSo = new StoredObject();
|
|
||||||
}
|
|
||||||
// define the newly created resource as null-resource
|
// define the newly created resource as null-resource
|
||||||
nullSo.setNullResource(true);
|
nullSo.setNullResource(true);
|
||||||
|
|
||||||
|
@ -419,13 +416,8 @@ public class DoLock extends AbstractMethod {
|
||||||
|
|
||||||
if (currentNode.getNodeType() == Node.ELEMENT_NODE
|
if (currentNode.getNodeType() == Node.ELEMENT_NODE
|
||||||
|| currentNode.getNodeType() == Node.TEXT_NODE) {
|
|| currentNode.getNodeType() == Node.TEXT_NODE) {
|
||||||
if (currentNode.getFirstChild() != null) {
|
|
||||||
_lockOwner = currentNode.getFirstChild()
|
_lockOwner = currentNode.getFirstChild()
|
||||||
.getNodeValue();
|
.getNodeValue();
|
||||||
} else {
|
|
||||||
_lockOwner = currentNode.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -72,11 +72,6 @@ public class DoUnlock extends DeterminableMethod {
|
||||||
if (_resourceLocks.unlock(transaction, lockId, owner)) {
|
if (_resourceLocks.unlock(transaction, lockId, owner)) {
|
||||||
StoredObject so = _store.getStoredObject(
|
StoredObject so = _store.getStoredObject(
|
||||||
transaction, path);
|
transaction, path);
|
||||||
if (so == null) {
|
|
||||||
resp.sendError(HttpServletResponse.SC_NOT_FOUND);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (so.isNullResource()) {
|
if (so.isNullResource()) {
|
||||||
_store.removeObject(transaction, path);
|
_store.removeObject(transaction, path);
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,5 +5,3 @@ logging.file.name=webdav.log
|
||||||
aliyundrive.auth.enable=true
|
aliyundrive.auth.enable=true
|
||||||
aliyundrive.auth.user-name=admin
|
aliyundrive.auth.user-name=admin
|
||||||
aliyundrive.auth.password=admin
|
aliyundrive.auth.password=admin
|
||||||
|
|
||||||
server.tomcat.connection-timeout=1d
|
|
Loading…
Reference in New Issue