作者 钟来

初始提交

  1 +# Compiled class file
  2 +logs/*
  3 +config.json
  4 +
  5 +# Log file
  6 +*.log
  7 +
  8 +# BlueJ files
  9 +*.ctxt
  10 +
  11 +# Mobile Tools for Java (J2ME)
  12 +.mtj.tmp/
  13 +
  14 +# Package Files #
  15 +*.jar
  16 +*.war
  17 +*.ear
  18 +*.zip
  19 +*.tar.gz
  20 +*.rar
  21 +
  22 +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
  23 +hs_err_pid*
  24 +
  25 +target/
  26 +pom.xml.tag
  27 +pom.xml.releaseBackup
  28 +pom.xml.versionsBackup
  29 +pom.xml.next
  30 +release.properties
  31 +dependency-reduced-pom.xml
  32 +buildNumber.properties
  33 +.mvn/timing.properties
  34 +
  35 +# Avoid ignoring Maven wrapper jar file (.jar files are usually ignored)
  36 +!/.mvn/wrapper/maven-wrapper.jar
  37 +
  38 +### IntelliJ IDEA ###
  39 +.idea
  40 +*.iws
  41 +*.iml
  42 +*.ipr
  43 +
  44 +.DS_Store
  45 +!.mvn/wrapper/maven-wrapper.jar
  46 +
  47 +### STS ###
  48 +.apt_generated
  49 +.classpath
  50 +.factorypath
  51 +.project
  52 +.settings
  53 +.springBeans
  54 +
  55 +### NetBeans ###
  56 +nbproject/private/
  57 +build/
  58 +nbbuild/
  59 +dist/
  60 +nbdist/
  61 +.nb-gradle/
  1 +MIT License
  2 +
  3 +Copyright (c) 2017 潘滔
  4 +
  5 +Permission is hereby granted, free of charge, to any person obtaining a copy
  6 +of this software and associated documentation files (the "Software"), to deal
  7 +in the Software without restriction, including without limitation the rights
  8 +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9 +copies of the Software, and to permit persons to whom the Software is
  10 +furnished to do so, subject to the following conditions:
  11 +
  12 +The above copyright notice and this permission notice shall be included in all
  13 +copies or substantial portions of the Software.
  14 +
  15 +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16 +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17 +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18 +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19 +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  20 +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  21 +SOFTWARE.
  1 +#### 七牛云对象存储管理工具介绍
  2 +
  3 +- 设置文件前缀
  4 +
  5 + 路径前缀可以用来分类文件,例如: `image/jpg/`your-file-name.jpg。
  6 +
  7 +- 添加存储空间
  8 +
  9 + 添加存储空间,需要同时指定空间名称,空间域名以及所在区域。
  10 +
  11 +- 重置密钥
  12 +
  13 + 如果你修改了KEY,可以在此处修改密钥(第一次使用也需要重置KEY)。
  14 +
  15 + > 说明:出于安全考虑,建议您周期性地更换密钥。[查看我的密钥](https://portal.qiniu.com/user/key) [密钥安全使用须知](https://developer.qiniu.com/kodo/kb/1334/the-access-key-secret-key-encryption-key-safe-use-instructions)
  16 +
  17 +- 配置文件
  18 +
  19 + 与应用在同级目录,其中 `config.json` 为配置文件
  20 +
  21 +- 文件上传
  22 +
  23 + 除了可(支持断点和覆盖)上传本地文件外,还可抓取网络文件到空间中
  24 +
  25 +- 刷新列表
  26 +
  27 + 刷新当前存储空间的资源列表
  28 +
  29 +- 复制链接
  30 +
  31 + 复制你选中文件的外链
  32 +
  33 +- 删除文件
  34 +
  35 + 从存储空间中删除你选中的所有文件
  36 +
  37 +- 移动文件
  38 +
  39 + 移动(或复制)选中的所有文件到指定的存储空间中(目前七牛还不支持跨区域移动文件)
  40 +
  41 + > 说明:勾选“保存文件副本”时表示当前操作为复制,不勾选时表示移动(会删除本存储空间的文件),默认勾选。
  42 +
  43 +- 生存时间
  44 +
  45 + 设置选中文件的生存时间,到期后七牛会自动删除这些文件
  46 +
  47 +- 更新镜像
  48 +
  49 + 此功能首先需要你配置了镜像存储
  50 +
  51 + > 官方解释:对于配置了镜像存储的空间,如果镜像源站更新了文件内容,则默认情况下,七牛不会再主动从客户镜像源站同步新的副本,这个时候就需要利用这个prefetch接口来主动地将空间中的文件和更新后的源站副本进行同步。
  52 +
  53 +- 公有下载
  54 +
  55 + 直接下载选中的文件(私有的存储空间不可用)
  56 +
  57 +- 私有下载
  58 +
  59 + 下载选中的私有存储空间的文件
  60 +
  61 +- 打开文件
  62 +
  63 + 用浏览器打开你选中的文件
  64 +
  65 +- 文件刷新
  66 +
  67 + 从七牛云镜像源刷新你选中的文件,保证用户下载的是最新上传的文件,而不是之前的旧版本。
  68 +
  69 +- 日志下载
  70 +
  71 + 从七牛下载指定日期的操作日志
  72 +
  73 +> 说明:操作文件时,需要选中文件才能操作(支持多选)。由于下载私有空间的文件需要临时授权,所以文件的下载分为私有下载(生成临时授权然后下载文件)和公有下载(直接下载文件)。
  74 +另外数据统计的时间跨度不超过31天,否则无法获取数据,这是七牛官方规定的。
  75 +
  76 +- 参考 [官方JavaSDK文档](https://developer.qiniu.com/kodo/sdk/1239/java)
  1 +theme: jekyll-theme-hacker
  1 +#!/bin/sh
  2 +# ----------------------------------------------------------------------------
  3 +# Licensed to the Apache Software Foundation (ASF) under one
  4 +# or more contributor license agreements. See the NOTICE file
  5 +# distributed with this work for additional information
  6 +# regarding copyright ownership. The ASF licenses this file
  7 +# to you under the Apache License, Version 2.0 (the
  8 +# "License"); you may not use this file except in compliance
  9 +# with the License. You may obtain a copy of the License at
  10 +#
  11 +# http://www.apache.org/licenses/LICENSE-2.0
  12 +#
  13 +# Unless required by applicable law or agreed to in writing,
  14 +# software distributed under the License is distributed on an
  15 +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  16 +# KIND, either express or implied. See the License for the
  17 +# specific language governing permissions and limitations
  18 +# under the License.
  19 +# ----------------------------------------------------------------------------
  20 +
  21 +# ----------------------------------------------------------------------------
  22 +# Maven2 Start Up Batch script
  23 +#
  24 +# Required ENV vars:
  25 +# ------------------
  26 +# JAVA_HOME - location of a JDK home dir
  27 +#
  28 +# Optional ENV vars
  29 +# -----------------
  30 +# M2_HOME - location of maven2's installed home dir
  31 +# MAVEN_OPTS - parameters passed to the Java VM when running Maven
  32 +# e.g. to debug Maven itself, use
  33 +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
  34 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files
  35 +# ----------------------------------------------------------------------------
  36 +
  37 +if [ -z "$MAVEN_SKIP_RC" ] ; then
  38 +
  39 + if [ -f /etc/mavenrc ] ; then
  40 + . /etc/mavenrc
  41 + fi
  42 +
  43 + if [ -f "$HOME/.mavenrc" ] ; then
  44 + . "$HOME/.mavenrc"
  45 + fi
  46 +
  47 +fi
  48 +
  49 +# OS specific support. $var _must_ be set to either true or false.
  50 +cygwin=false;
  51 +darwin=false;
  52 +mingw=false
  53 +case "`uname`" in
  54 + CYGWIN*) cygwin=true ;;
  55 + MINGW*) mingw=true;;
  56 + Darwin*) darwin=true
  57 + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
  58 + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html
  59 + if [ -z "$JAVA_HOME" ]; then
  60 + if [ -x "/usr/libexec/java_home" ]; then
  61 + export JAVA_HOME="`/usr/libexec/java_home`"
  62 + else
  63 + export JAVA_HOME="/Library/Java/Home"
  64 + fi
  65 + fi
  66 + ;;
  67 +esac
  68 +
  69 +if [ -z "$JAVA_HOME" ] ; then
  70 + if [ -r /etc/gentoo-release ] ; then
  71 + JAVA_HOME=`java-config --jre-home`
  72 + fi
  73 +fi
  74 +
  75 +if [ -z "$M2_HOME" ] ; then
  76 + ## resolve links - $0 may be a link to maven's home
  77 + PRG="$0"
  78 +
  79 + # need this for relative symlinks
  80 + while [ -h "$PRG" ] ; do
  81 + ls=`ls -ld "$PRG"`
  82 + link=`expr "$ls" : '.*-> \(.*\)$'`
  83 + if expr "$link" : '/.*' > /dev/null; then
  84 + PRG="$link"
  85 + else
  86 + PRG="`dirname "$PRG"`/$link"
  87 + fi
  88 + done
  89 +
  90 + saveddir=`pwd`
  91 +
  92 + M2_HOME=`dirname "$PRG"`/..
  93 +
  94 + # make it fully qualified
  95 + M2_HOME=`cd "$M2_HOME" && pwd`
  96 +
  97 + cd "$saveddir"
  98 + # echo Using m2 at $M2_HOME
  99 +fi
  100 +
  101 +# For Cygwin, ensure paths are in UNIX format before anything is touched
  102 +if $cygwin ; then
  103 + [ -n "$M2_HOME" ] &&
  104 + M2_HOME=`cygpath --unix "$M2_HOME"`
  105 + [ -n "$JAVA_HOME" ] &&
  106 + JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
  107 + [ -n "$CLASSPATH" ] &&
  108 + CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
  109 +fi
  110 +
  111 +# For Migwn, ensure paths are in UNIX format before anything is touched
  112 +if $mingw ; then
  113 + [ -n "$M2_HOME" ] &&
  114 + M2_HOME="`(cd "$M2_HOME"; pwd)`"
  115 + [ -n "$JAVA_HOME" ] &&
  116 + JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
  117 + # TODO classpath?
  118 +fi
  119 +
  120 +if [ -z "$JAVA_HOME" ]; then
  121 + javaExecutable="`which javac`"
  122 + if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
  123 + # readlink(1) is not available as standard on Solaris 10.
  124 + readLink=`which readlink`
  125 + if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
  126 + if $darwin ; then
  127 + javaHome="`dirname \"$javaExecutable\"`"
  128 + javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
  129 + else
  130 + javaExecutable="`readlink -f \"$javaExecutable\"`"
  131 + fi
  132 + javaHome="`dirname \"$javaExecutable\"`"
  133 + javaHome=`expr "$javaHome" : '\(.*\)/bin'`
  134 + JAVA_HOME="$javaHome"
  135 + export JAVA_HOME
  136 + fi
  137 + fi
  138 +fi
  139 +
  140 +if [ -z "$JAVACMD" ] ; then
  141 + if [ -n "$JAVA_HOME" ] ; then
  142 + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
  143 + # IBM's JDK on AIX uses strange locations for the executables
  144 + JAVACMD="$JAVA_HOME/jre/sh/java"
  145 + else
  146 + JAVACMD="$JAVA_HOME/bin/java"
  147 + fi
  148 + else
  149 + JAVACMD="`which java`"
  150 + fi
  151 +fi
  152 +
  153 +if [ ! -x "$JAVACMD" ] ; then
  154 + echo "Error: JAVA_HOME is not defined correctly." >&2
  155 + echo " We cannot execute $JAVACMD" >&2
  156 + exit 1
  157 +fi
  158 +
  159 +if [ -z "$JAVA_HOME" ] ; then
  160 + echo "Warning: JAVA_HOME environment variable is not set."
  161 +fi
  162 +
  163 +CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
  164 +
  165 +# traverses directory structure from process work directory to filesystem root
  166 +# first directory with .mvn subdirectory is considered project base directory
  167 +find_maven_basedir() {
  168 +
  169 + if [ -z "$1" ]
  170 + then
  171 + echo "Path not specified to find_maven_basedir"
  172 + return 1
  173 + fi
  174 +
  175 + basedir="$1"
  176 + wdir="$1"
  177 + while [ "$wdir" != '/' ] ; do
  178 + if [ -d "$wdir"/.mvn ] ; then
  179 + basedir=$wdir
  180 + break
  181 + fi
  182 + # workaround for JBEAP-8937 (on Solaris 10/Sparc)
  183 + if [ -d "${wdir}" ]; then
  184 + wdir=`cd "$wdir/.."; pwd`
  185 + fi
  186 + # end of workaround
  187 + done
  188 + echo "${basedir}"
  189 +}
  190 +
  191 +# concatenates all lines of a file
  192 +concat_lines() {
  193 + if [ -f "$1" ]; then
  194 + echo "$(tr -s '\n' ' ' < "$1")"
  195 + fi
  196 +}
  197 +
  198 +BASE_DIR=`find_maven_basedir "$(pwd)"`
  199 +if [ -z "$BASE_DIR" ]; then
  200 + exit 1;
  201 +fi
  202 +
  203 +export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}
  204 +echo $MAVEN_PROJECTBASEDIR
  205 +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
  206 +
  207 +# For Cygwin, switch paths to Windows format before running java
  208 +if $cygwin; then
  209 + [ -n "$M2_HOME" ] &&
  210 + M2_HOME=`cygpath --path --windows "$M2_HOME"`
  211 + [ -n "$JAVA_HOME" ] &&
  212 + JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
  213 + [ -n "$CLASSPATH" ] &&
  214 + CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
  215 + [ -n "$MAVEN_PROJECTBASEDIR" ] &&
  216 + MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"`
  217 +fi
  218 +
  219 +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
  220 +
  221 +exec "$JAVACMD" \
  222 + $MAVEN_OPTS \
  223 + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
  224 + "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
  225 + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"
  1 +@REM ----------------------------------------------------------------------------
  2 +@REM Licensed to the Apache Software Foundation (ASF) under one
  3 +@REM or more contributor license agreements. See the NOTICE file
  4 +@REM distributed with this work for additional information
  5 +@REM regarding copyright ownership. The ASF licenses this file
  6 +@REM to you under the Apache License, Version 2.0 (the
  7 +@REM "License"); you may not use this file except in compliance
  8 +@REM with the License. You may obtain a copy of the License at
  9 +@REM
  10 +@REM http://www.apache.org/licenses/LICENSE-2.0
  11 +@REM
  12 +@REM Unless required by applicable law or agreed to in writing,
  13 +@REM software distributed under the License is distributed on an
  14 +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  15 +@REM KIND, either express or implied. See the License for the
  16 +@REM specific language governing permissions and limitations
  17 +@REM under the License.
  18 +@REM ----------------------------------------------------------------------------
  19 +
  20 +@REM ----------------------------------------------------------------------------
  21 +@REM Maven2 Start Up Batch script
  22 +@REM
  23 +@REM Required ENV vars:
  24 +@REM JAVA_HOME - location of a JDK home dir
  25 +@REM
  26 +@REM Optional ENV vars
  27 +@REM M2_HOME - location of maven2's installed home dir
  28 +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
  29 +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending
  30 +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
  31 +@REM e.g. to debug Maven itself, use
  32 +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
  33 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
  34 +@REM ----------------------------------------------------------------------------
  35 +
  36 +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
  37 +@echo off
  38 +@REM enable echoing my setting MAVEN_BATCH_ECHO to 'on'
  39 +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
  40 +
  41 +@REM set %HOME% to equivalent of $HOME
  42 +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
  43 +
  44 +@REM Execute a user defined script before this one
  45 +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
  46 +@REM check for pre script, once with legacy .bat ending and once with .cmd ending
  47 +if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat"
  48 +if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd"
  49 +:skipRcPre
  50 +
  51 +@setlocal
  52 +
  53 +set ERROR_CODE=0
  54 +
  55 +@REM To isolate internal variables from possible post scripts, we use another setlocal
  56 +@setlocal
  57 +
  58 +@REM ==== START VALIDATION ====
  59 +if not "%JAVA_HOME%" == "" goto OkJHome
  60 +
  61 +echo.
  62 +echo Error: JAVA_HOME not found in your environment. >&2
  63 +echo Please set the JAVA_HOME variable in your environment to match the >&2
  64 +echo location of your Java installation. >&2
  65 +echo.
  66 +goto error
  67 +
  68 +:OkJHome
  69 +if exist "%JAVA_HOME%\bin\java.exe" goto init
  70 +
  71 +echo.
  72 +echo Error: JAVA_HOME is set to an invalid directory. >&2
  73 +echo JAVA_HOME = "%JAVA_HOME%" >&2
  74 +echo Please set the JAVA_HOME variable in your environment to match the >&2
  75 +echo location of your Java installation. >&2
  76 +echo.
  77 +goto error
  78 +
  79 +@REM ==== END VALIDATION ====
  80 +
  81 +:init
  82 +
  83 +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
  84 +@REM Fallback to current working directory if not found.
  85 +
  86 +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
  87 +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
  88 +
  89 +set EXEC_DIR=%CD%
  90 +set WDIR=%EXEC_DIR%
  91 +:findBaseDir
  92 +IF EXIST "%WDIR%"\.mvn goto baseDirFound
  93 +cd ..
  94 +IF "%WDIR%"=="%CD%" goto baseDirNotFound
  95 +set WDIR=%CD%
  96 +goto findBaseDir
  97 +
  98 +:baseDirFound
  99 +set MAVEN_PROJECTBASEDIR=%WDIR%
  100 +cd "%EXEC_DIR%"
  101 +goto endDetectBaseDir
  102 +
  103 +:baseDirNotFound
  104 +set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
  105 +cd "%EXEC_DIR%"
  106 +
  107 +:endDetectBaseDir
  108 +
  109 +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
  110 +
  111 +@setlocal EnableExtensions EnableDelayedExpansion
  112 +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
  113 +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
  114 +
  115 +:endReadAdditionalConfig
  116 +
  117 +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
  118 +
  119 +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
  120 +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
  121 +
  122 +%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
  123 +if ERRORLEVEL 1 goto error
  124 +goto end
  125 +
  126 +:error
  127 +set ERROR_CODE=1
  128 +
  129 +:end
  130 +@endlocal & set ERROR_CODE=%ERROR_CODE%
  131 +
  132 +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost
  133 +@REM check for post script, once with legacy .bat ending and once with .cmd ending
  134 +if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat"
  135 +if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd"
  136 +:skipRcPost
  137 +
  138 +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
  139 +if "%MAVEN_BATCH_PAUSE%" == "on" pause
  140 +
  141 +if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE%
  142 +
  143 +exit /B %ERROR_CODE%
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3 + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  4 + <modelVersion>4.0.0</modelVersion>
  5 +
  6 + <groupId>com.zhazhapan</groupId>
  7 + <artifactId>qiniu</artifactId>
  8 + <version>1.0.8</version>
  9 + <packaging>jar</packaging>
  10 +
  11 + <name>qiniu</name>
  12 + <description>七牛同步工具</description>
  13 +
  14 + <properties>
  15 + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  16 + <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
  17 + <java.version>1.8</java.version>
  18 + </properties>
  19 +
  20 + <dependencies>
  21 + <dependency>
  22 + <groupId>com.qiniu</groupId>
  23 + <artifactId>qiniu-java-sdk</artifactId>
  24 + <version>[7.2.0, 7.2.99]</version>
  25 + </dependency>
  26 + <dependency>
  27 + <groupId>com.zhazhapan</groupId>
  28 + <artifactId>util</artifactId>
  29 + <version>1.1.1</version>
  30 + </dependency>
  31 + <dependency>
  32 + <groupId>org.projectlombok</groupId>
  33 + <artifactId>lombok</artifactId>
  34 + <version>1.18.2</version>
  35 + </dependency>
  36 + </dependencies>
  37 +
  38 + <build>
  39 + <plugins>
  40 + <plugin>
  41 + <groupId>org.apache.maven.plugins</groupId>
  42 + <artifactId>maven-compiler-plugin</artifactId>
  43 + <configuration>
  44 + <source>1.8</source>
  45 + <target>1.8</target>
  46 + </configuration>
  47 + </plugin>
  48 + <plugin>
  49 + <groupId>org.apache.maven.plugins</groupId>
  50 + <artifactId>maven-shade-plugin</artifactId>
  51 + <version>2.3</version>
  52 + <executions>
  53 + <execution>
  54 + <phase>package</phase>
  55 + <goals>
  56 + <goal>shade</goal>
  57 + </goals>
  58 + <configuration>
  59 + <transformers>
  60 + <transformer
  61 + implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
  62 + <mainClass>org.code4everything.qiniu.QiniuApplication</mainClass>
  63 + </transformer>
  64 + </transformers>
  65 + <artifactSet>
  66 + </artifactSet>
  67 + </configuration>
  68 + </execution>
  69 + </executions>
  70 + </plugin>
  71 + </plugins>
  72 + </build>
  73 +</project>
  1 +package org.code4everything.qiniu;
  2 +
  3 +import com.zhazhapan.util.ThreadPool;
  4 +import javafx.application.Application;
  5 +import javafx.fxml.FXMLLoader;
  6 +import javafx.scene.Scene;
  7 +import javafx.scene.control.ButtonType;
  8 +import javafx.scene.image.Image;
  9 +import javafx.scene.layout.VBox;
  10 +import javafx.stage.Stage;
  11 +import org.apache.log4j.Logger;
  12 +import org.code4everything.qiniu.constant.QiniuValueConsts;
  13 +import org.code4everything.qiniu.model.ConfigBean;
  14 +import org.code4everything.qiniu.util.ConfigUtils;
  15 +import org.code4everything.qiniu.util.DialogUtils;
  16 +
  17 +import java.util.Optional;
  18 +import java.util.concurrent.LinkedBlockingQueue;
  19 +
  20 +/**
  21 + * @author pantao
  22 + */
  23 +public class QiniuApplication extends Application {
  24 +
  25 + private static final Logger LOGGER = Logger.getLogger(QiniuApplication.class);
  26 +
  27 + private static Stage stage = null;
  28 +
  29 + private static ConfigBean configBean;
  30 +
  31 + public static Stage getStage() {
  32 + return stage;
  33 + }
  34 +
  35 + public static ConfigBean getConfigBean() {
  36 + return configBean;
  37 + }
  38 +
  39 + public static void setConfigBean(ConfigBean configBean) {
  40 + QiniuApplication.configBean = configBean;
  41 + }
  42 +
  43 + /**
  44 + * 主程序入口
  45 + */
  46 + public static void main(String[] args) {
  47 + // 设置线程池大小
  48 + ThreadPool.setMaximumPoolSize(10);
  49 + // 设置线程池最大排队大小
  50 + ThreadPool.setWorkQueue(new LinkedBlockingQueue<>(1024));
  51 + ThreadPool.init();
  52 + // 启动 JavaFX 应用
  53 + launch(args);
  54 + }
  55 +
  56 + /**
  57 + * 由 JavaFX 调用
  58 + */
  59 + @Override
  60 + public void start(Stage stage) {
  61 + try {
  62 + // 加载视图页面
  63 + VBox root = FXMLLoader.load(getClass().getResource(QiniuValueConsts.QINIU_VIEW_URL));
  64 + Scene scene = new Scene(root);
  65 + stage.setScene(scene);
  66 + } catch (Exception e) {
  67 + LOGGER.error("init stage error: " + e.getMessage());
  68 + DialogUtils.showFatalError(QiniuValueConsts.INIT_APP_ERROR_HEADER, e);
  69 + }
  70 + // 设置图标
  71 + stage.getIcons().add(new Image(getClass().getResourceAsStream("/image/qiniu.png")));
  72 + stage.setTitle(QiniuValueConsts.MAIN_TITLE);
  73 + // 设置关闭窗口事件
  74 + stage.setOnCloseRequest(event -> {
  75 + Optional<ButtonType> result = DialogUtils.showConfirmation(QiniuValueConsts.CONFIRM_EXIT);
  76 + if (result.isPresent() && result.get() != ButtonType.OK) {
  77 + // 取消退出事件
  78 + event.consume();
  79 + return;
  80 + }
  81 + // 退出程序
  82 + ThreadPool.executor.shutdown();
  83 + System.exit(0);
  84 + });
  85 + QiniuApplication.stage = stage;
  86 + stage.show();
  87 + // 加载配置文件
  88 + ConfigUtils.loadConfig();
  89 + }
  90 +}
  1 +package org.code4everything.qiniu.api;
  2 +
  3 +import com.qiniu.cdn.CdnManager;
  4 +import com.qiniu.common.Zone;
  5 +import com.qiniu.storage.BucketManager;
  6 +import com.qiniu.storage.Configuration;
  7 +import com.qiniu.storage.UploadManager;
  8 +import com.qiniu.storage.persistent.FileRecorder;
  9 +import com.qiniu.util.Auth;
  10 +import com.zhazhapan.util.Utils;
  11 +import org.apache.log4j.Logger;
  12 +
  13 +import java.io.IOException;
  14 +import java.nio.file.Paths;
  15 +import java.util.HashMap;
  16 +import java.util.Map;
  17 +
  18 +/**
  19 + * @author pantao
  20 + */
  21 +public class SdkConfigurer {
  22 +
  23 + private static final String[] BUCKET_NAME_ARRAY = {"华东", "华北", "华南", "北美"};
  24 +
  25 + private static final Map<String, Zone> ZONE = new HashMap<>();
  26 +
  27 + private static final Logger LOGGER = Logger.getLogger(SdkConfigurer.class);
  28 +
  29 + private static Auth auth = null;
  30 +
  31 + private static UploadManager uploadManager = null;
  32 +
  33 + private static BucketManager bucketManager = null;
  34 +
  35 + private static CdnManager cdnManager = null;
  36 +
  37 + static {
  38 + // 加载空间区域
  39 + ZONE.put(BUCKET_NAME_ARRAY[0], Zone.zone0());
  40 + ZONE.put(BUCKET_NAME_ARRAY[1], Zone.zone1());
  41 + ZONE.put(BUCKET_NAME_ARRAY[2], Zone.zone2());
  42 + ZONE.put(BUCKET_NAME_ARRAY[3], Zone.zoneNa0());
  43 + }
  44 +
  45 + private SdkConfigurer() {}
  46 +
  47 + public static CdnManager getCdnManager() {
  48 + return cdnManager;
  49 + }
  50 +
  51 + public static BucketManager getBucketManager() {
  52 + return bucketManager;
  53 + }
  54 +
  55 + public static UploadManager getUploadManager() {
  56 + return uploadManager;
  57 + }
  58 +
  59 + public static Auth getAuth() {
  60 + return auth;
  61 + }
  62 +
  63 + /**
  64 + * 创建上传需要的Auth
  65 + */
  66 + public static void createAuth(String accessKey, String secretKey) {
  67 + auth = Auth.create(accessKey, secretKey);
  68 + cdnManager = new CdnManager(auth);
  69 + }
  70 +
  71 + /**
  72 + * 配置文件上传环境,不再做网络检查,请执行保证网络通畅
  73 + */
  74 + public static boolean configUploadEnv(String zone, String bucket) {
  75 + // 构造一个带指定Zone对象的配置类
  76 + Configuration configuration = new Configuration(SdkConfigurer.ZONE.get(zone));
  77 + // 生成上传凭证,然后准备上传
  78 + String workDir = Paths.get(Utils.getCurrentWorkDir(), bucket).toString();
  79 + try {
  80 + FileRecorder fileRecorder = new FileRecorder(workDir);
  81 + uploadManager = new UploadManager(configuration, fileRecorder);
  82 + } catch (IOException e) {
  83 + LOGGER.warn("load work directory failed, can't use file recorder");
  84 + uploadManager = new UploadManager(configuration);
  85 + }
  86 + bucketManager = new BucketManager(auth, configuration);
  87 + return true;
  88 + }
  89 +}
  1 +package org.code4everything.qiniu.api;
  2 +
  3 +import com.qiniu.cdn.CdnResult;
  4 +import com.qiniu.cdn.CdnResult.LogData;
  5 +import com.qiniu.common.QiniuException;
  6 +import com.qiniu.http.Response;
  7 +import com.qiniu.storage.BucketManager;
  8 +import com.qiniu.storage.model.BatchStatus;
  9 +
  10 +import java.util.Map;
  11 +
  12 +/**
  13 + * @author pantao
  14 + */
  15 +public class SdkManager {
  16 +
  17 + /**
  18 + * 自定义私有链接过期时间
  19 + */
  20 + private static final long EXPIRE_IN_SECONDS = 24 * 60 * 60L;
  21 +
  22 + /**
  23 + * 文件列表大小
  24 + */
  25 + private static final int LIST_SIZE = 1000;
  26 +
  27 + /**
  28 + * 获取空间带宽统计
  29 + */
  30 + public CdnResult.BandwidthResult getBandwidthData(String[] domains, String startDate, String endDate) throws QiniuException {
  31 + return SdkConfigurer.getCdnManager().getBandwidthData(domains, startDate, endDate, "day");
  32 + }
  33 +
  34 + /**
  35 + * 获取空间的流量统计
  36 + */
  37 + public CdnResult.FluxResult getFluxData(String[] domains, String startDate, String endDate) throws QiniuException {
  38 + return SdkConfigurer.getCdnManager().getFluxData(domains, startDate, endDate, "day");
  39 + }
  40 +
  41 + /**
  42 + * 日志下载,CDN 相关
  43 + */
  44 + public Map<String, LogData[]> listCdnLog(String[] domains, String logDate) throws QiniuException {
  45 + return SdkConfigurer.getCdnManager().getCdnLogList(domains, logDate).data;
  46 + }
  47 +
  48 + /**
  49 + * 刷新文件,CDN 相关
  50 + */
  51 + public void refreshFile(String[] files) throws QiniuException {
  52 + // 单次方法调用刷新的链接不可以超过100个
  53 + SdkConfigurer.getCdnManager().refreshUrls(files);
  54 + }
  55 +
  56 + /**
  57 + * 私有下载
  58 + */
  59 + public String getPrivateUrl(String publicUrl) {
  60 + return SdkConfigurer.getAuth().privateDownloadUrl(publicUrl, EXPIRE_IN_SECONDS);
  61 + }
  62 +
  63 + /**
  64 + * 更新镜像源
  65 + */
  66 + public void prefetch(String bucket, String key) throws QiniuException {
  67 + SdkConfigurer.getBucketManager().prefetch(bucket, key);
  68 + }
  69 +
  70 + /**
  71 + * 设置文件生存时间
  72 + */
  73 + public void deleteAfterDays(String bucket, String key, int days) throws QiniuException {
  74 + SdkConfigurer.getBucketManager().deleteAfterDays(bucket, key, days);
  75 + }
  76 +
  77 + /**
  78 + * 重命名文件
  79 + */
  80 + public void renameFile(String bucket, String oldName, String newName) throws QiniuException {
  81 + moveFile(bucket, oldName, bucket, newName);
  82 + }
  83 +
  84 + /**
  85 + * 移动文件
  86 + */
  87 + public void moveFile(String srcBucket, String fromKey, String destBucket, String toKey) throws QiniuException {
  88 + moveOrCopyFile(srcBucket, fromKey, destBucket, toKey, FileAction.MOVE);
  89 + }
  90 +
  91 + /**
  92 + * 移动或复制文件
  93 + */
  94 + public void moveOrCopyFile(String srcBucket, String srcKey, String destBucket, String destKey,
  95 + FileAction fileAction) throws QiniuException {
  96 + if (FileAction.COPY == fileAction) {
  97 + SdkConfigurer.getBucketManager().copy(srcBucket, srcKey, destBucket, destKey, true);
  98 + } else {
  99 + SdkConfigurer.getBucketManager().move(srcBucket, srcKey, destBucket, destKey, true);
  100 + }
  101 + }
  102 +
  103 + /**
  104 + * 修改文件类型
  105 + */
  106 + public void changeMime(String fileName, String newType, String bucket) throws QiniuException {
  107 + SdkConfigurer.getBucketManager().changeMime(bucket, fileName, newType);
  108 + }
  109 +
  110 + /**
  111 + * 批量删除文件,单次批量请求的文件数量不得超过1000
  112 + */
  113 + public BatchStatus[] batchDelete(String bucket, String[] keys) throws QiniuException {
  114 + BucketManager.BatchOperations batchOperations = new BucketManager.BatchOperations();
  115 + batchOperations.addDeleteOp(bucket, keys);
  116 + Response response = SdkConfigurer.getBucketManager().batch(batchOperations);
  117 + return response.jsonToObject(BatchStatus[].class);
  118 + }
  119 +
  120 + /**
  121 + * 获取空间文件列表
  122 + */
  123 + public BucketManager.FileListIterator getFileListIterator(String bucket) {
  124 + return SdkConfigurer.getBucketManager().createFileListIterator(bucket, "", LIST_SIZE, "");
  125 + }
  126 +
  127 + public enum FileAction {
  128 + // 复制或移动文件
  129 + COPY, MOVE
  130 + }
  131 +}
  1 +package org.code4everything.qiniu.constant;
  2 +
  3 +import com.zhazhapan.util.Utils;
  4 +
  5 +import java.io.File;
  6 +
  7 +/**
  8 + * 常量类
  9 + *
  10 + * @author pantao
  11 + */
  12 +public class QiniuValueConsts {
  13 +
  14 + public static final String CONFIG_PATH = Utils.getCurrentWorkDir() + File.separator + "config.json";
  15 +
  16 + public static final String QINIU_VIEW_URL = "/view/Main.fxml";
  17 +
  18 + public static final String QINIU_KEY_URL = "https://portal.qiniu.com/user/key";
  19 +
  20 + public static final String MAIN_TITLE = "七牛云管理工具";
  21 +
  22 + public static final String INIT_APP_ERROR_HEADER = "初始化错误,无法继续运行";
  23 +
  24 + public static final String OK = "确定";
  25 +
  26 + public static final String CANCEL = "取消";
  27 +
  28 + public static final String BUCKET_NAME = "空间名称";
  29 +
  30 + public static final String BUCKET_ZONE_NAME = "存储区域";
  31 +
  32 + public static final String BUCKET_URL = "空间域名";
  33 +
  34 + public static final String[] BUCKET_NAME_ARRAY = {"华东", "华北", "华南", "北美"};
  35 +
  36 + public static final String FILE_CHOOSER_TITLE = "选择需要上传的文件";
  37 +
  38 + public static final String OPEN_FILE_ERROR = "打开文件失败";
  39 +
  40 + public static final String UPLOAD_ERROR = "上传文件失败";
  41 +
  42 + public static final String UPLOADING = "文件上传中,请耐心等待。。。。。。\r\n";
  43 +
  44 + public static final String NEED_CHOOSE_BUCKET_OR_FILE = "请先选择一个存储空间或文件";
  45 +
  46 + public static final String CONFIG_UPLOAD_ENVIRONMENT = "正在配置文件上传环境,请耐心等待。。。。。。\r\n";
  47 +
  48 + public static final String RELOAD_CONFIG = "是否重新载入配置文件?";
  49 +
  50 + public static final String DOMAIN_CONFIG_ERROR = "您还没有正确地配置空间域名";
  51 +
  52 + public static final String REFRESH_SUCCESS = "刷新资源列表成功";
  53 +
  54 + public static final String DELETE_ERROR = "删除文件时发生异常";
  55 +
  56 + public static final String CHANGE_FILE_TYPE_ERROR = "删除文件发生异常";
  57 +
  58 + public static final String MOVE_OR_RENAME_ERROR = "移动或重命名文件失败";
  59 +
  60 + public static final String FILE_NAME = "文件名";
  61 +
  62 + public static final String COPY_AS = "保存文件副本";
  63 +
  64 + public static final String FILE_LIFE = "文件生存时间(天)";
  65 +
  66 + public static final String UPDATE_ERROR = "更新镜像源失败";
  67 +
  68 + public static final String DEFAULT_FILE_LIFE = "365";
  69 +
  70 + public static final String CONFIG_DOWNLOAD_PATH = "配置文件下载路径";
  71 +
  72 + public static final String INPUT_LOG_DATE = "请输入日志的日期";
  73 +
  74 + public static final String BUCKET_FLUX_ERROR = "获取空间流量统计失败";
  75 +
  76 + public static final String BUCKET_BAND_ERROR = "获取空间带宽统计失败";
  77 +
  78 + public static final String BUCKET_FLUX_COUNT = "空间流量(KB)";
  79 +
  80 + public static final String BUCKET_BANDWIDTH_COUNT = "空间带宽(KB)";
  81 +
  82 + public static final long DATE_SPAN_OF_THIRTY_ONE = 31 * 24 * 60 * 60 * 1000L;
  83 +
  84 + public static final String CONFIRM_EXIT = "确定退出?";
  85 +
  86 + private QiniuValueConsts() {}
  87 +}
  1 +package org.code4everything.qiniu.controller;
  2 +
  3 +import cn.hutool.core.date.DateUtil;
  4 +import cn.hutool.core.util.StrUtil;
  5 +import com.qiniu.common.QiniuException;
  6 +import com.zhazhapan.util.Checker;
  7 +import com.zhazhapan.util.Formatter;
  8 +import com.zhazhapan.util.ThreadPool;
  9 +import com.zhazhapan.util.Utils;
  10 +import javafx.application.Platform;
  11 +import javafx.collections.FXCollections;
  12 +import javafx.collections.ObservableList;
  13 +import javafx.fxml.FXML;
  14 +import javafx.scene.chart.AreaChart;
  15 +import javafx.scene.control.*;
  16 +import javafx.scene.control.Label;
  17 +import javafx.scene.control.TextArea;
  18 +import javafx.scene.control.TextField;
  19 +import javafx.scene.control.cell.PropertyValueFactory;
  20 +import javafx.scene.control.cell.TextFieldTableCell;
  21 +import javafx.scene.input.DragEvent;
  22 +import javafx.scene.input.TransferMode;
  23 +import javafx.stage.FileChooser;
  24 +import javafx.util.Pair;
  25 +import org.code4everything.qiniu.QiniuApplication;
  26 +import org.code4everything.qiniu.api.SdkConfigurer;
  27 +import org.code4everything.qiniu.api.SdkManager;
  28 +import org.code4everything.qiniu.constant.QiniuValueConsts;
  29 +import org.code4everything.qiniu.model.FileBean;
  30 +import org.code4everything.qiniu.service.QiniuService;
  31 +import org.code4everything.qiniu.util.ConfigUtils;
  32 +import org.code4everything.qiniu.util.DialogUtils;
  33 +import org.code4everything.qiniu.util.QiniuDialog;
  34 +import org.code4everything.qiniu.util.QiniuUtils;
  35 +
  36 +import java.awt.*;
  37 +import java.io.File;
  38 +import java.time.LocalDate;
  39 +import java.util.ArrayList;
  40 +import java.util.Date;
  41 +import java.util.List;
  42 +import java.util.Optional;
  43 +import java.util.regex.Pattern;
  44 +
  45 +/**
  46 + * 界面控制类
  47 + *
  48 + * @author pantao
  49 + */
  50 +public class MainController {
  51 +
  52 + private static final String UPLOAD_STATUS_TEMPLATE = "{}\tsuccess\t{}{}\t{}";
  53 +
  54 + private static MainController mainController = null;
  55 +
  56 + private final QiniuService service = new QiniuService();
  57 +
  58 + private final QiniuDialog dialog = new QiniuDialog();
  59 +
  60 + @FXML
  61 + public ComboBox<String> bucketCB;
  62 +
  63 + @FXML
  64 + public TextField zoneTF;
  65 +
  66 + @FXML
  67 + public TextArea uploadStatusTA;
  68 +
  69 + @FXML
  70 + public ComboBox<String> prefixCB;
  71 +
  72 + @FXML
  73 + public TableView<FileBean> resTV;
  74 +
  75 + @FXML
  76 + public TextField searchTF;
  77 +
  78 + @FXML
  79 + public CheckBox recursiveCB;
  80 +
  81 + @FXML
  82 + public CheckBox keepPathCB;
  83 +
  84 + private ObservableList<FileBean> resData = null;
  85 +
  86 + /**
  87 + * 空间总文件数
  88 + */
  89 + private int dataLength = 0;
  90 +
  91 + /**
  92 + * 空间使用总大小
  93 + */
  94 + private long dataSize = 0;
  95 +
  96 + @FXML
  97 + private TextArea selectedFileTA;
  98 +
  99 + @FXML
  100 + private TextField domainTF;
  101 +
  102 + @FXML
  103 + private TableColumn<FileBean, String> nameTC;
  104 +
  105 + @FXML
  106 + private TableColumn<FileBean, String> typeTC;
  107 +
  108 + @FXML
  109 + private TableColumn<FileBean, String> sizeTC;
  110 +
  111 + @FXML
  112 + private TableColumn<FileBean, String> timeTC;
  113 +
  114 + @FXML
  115 + private Label sizeLabel;
  116 +
  117 + @FXML
  118 + private Label lengthLabel;
  119 +
  120 + @FXML
  121 + private AreaChart<String, Long> fluxAC;
  122 +
  123 + @FXML
  124 + private AreaChart<String, Long> bandwidthAC;
  125 +
  126 + @FXML
  127 + private DatePicker startDP;
  128 +
  129 + @FXML
  130 + private DatePicker endDP;
  131 +
  132 + @FXML
  133 + private ComboBox<String> fluxUnitCB;
  134 +
  135 + @FXML
  136 + private ComboBox<String> bandwidthUnitCB;
  137 +
  138 + private String status = "";
  139 +
  140 + /**
  141 + * 父文件夹路径
  142 + */
  143 + private List<String> rootPath = new ArrayList<>();
  144 +
  145 + public static MainController getInstance() {
  146 + return mainController;
  147 + }
  148 +
  149 + public ObservableList<FileBean> getResData() {
  150 + return resData;
  151 + }
  152 +
  153 + public void setResData(ObservableList<FileBean> resData) {
  154 + this.resData = resData;
  155 + }
  156 +
  157 + public int getDataLength() {
  158 + return dataLength;
  159 + }
  160 +
  161 + public void setDataLength(int dataLength) {
  162 + this.dataLength = dataLength;
  163 + }
  164 +
  165 + public long getDataSize() {
  166 + return dataSize;
  167 + }
  168 +
  169 + public void setDataSize(long dataSize) {
  170 + this.dataSize = dataSize;
  171 + }
  172 +
  173 + /**
  174 + * 初始化
  175 + */
  176 + @FXML
  177 + private void initialize() {
  178 + mainController = this;
  179 + nameTC.setCellValueFactory(new PropertyValueFactory<>("name"));
  180 + // 设置文件名可编辑
  181 + nameTC.setCellFactory(TextFieldTableCell.forTableColumn());
  182 + nameTC.setOnEditCommit(value -> {
  183 + String name;
  184 + FileBean fileBean = value.getTableView().getItems().get(value.getTablePosition().getRow());
  185 + // 编辑后重命名文件
  186 + if (service.renameFile(bucketCB.getValue(), value.getOldValue(), value.getNewValue())) {
  187 + name = value.getNewValue();
  188 + } else {
  189 + name = value.getOldValue();
  190 + }
  191 + if (Checker.isNotEmpty(searchTF.getText())) {
  192 + resData.get(resData.indexOf(fileBean)).setName(name);
  193 + }
  194 + fileBean.setName(name);
  195 + });
  196 + typeTC.setCellValueFactory(new PropertyValueFactory<>("type"));
  197 + // 设置文件类型可编辑
  198 + typeTC.setCellFactory(TextFieldTableCell.forTableColumn());
  199 + typeTC.setOnEditCommit(value -> {
  200 + String type;
  201 + FileBean fileBean = value.getTableView().getItems().get(value.getTablePosition().getRow());
  202 + // 编辑后更新文件类型
  203 + if (service.changeType(fileBean.getName(), value.getNewValue(), bucketCB.getValue())) {
  204 + type = value.getNewValue();
  205 + } else {
  206 + type = value.getOldValue();
  207 + }
  208 + if (Checker.isNotEmpty(searchTF.getText())) {
  209 + resData.get(resData.indexOf(fileBean)).setType(type);
  210 + }
  211 + fileBean.setType(type);
  212 + });
  213 + sizeTC.setCellValueFactory(new PropertyValueFactory<>("size"));
  214 + timeTC.setCellValueFactory(new PropertyValueFactory<>("time"));
  215 + // 设置表格允许多选
  216 + resTV.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
  217 + // 设置默认的开始和结束日期,并事件刷新数据
  218 + endDP.setValue(LocalDate.now());
  219 + long startTime = System.currentTimeMillis() - QiniuValueConsts.DATE_SPAN_OF_THIRTY_ONE;
  220 + LocalDate localEndDate = Formatter.dateToLocalDate(new Date(startTime));
  221 + startDP.setValue(localEndDate);
  222 + // 设置桶下拉框改变事件,改变后配置新的上传环境
  223 + bucketCB.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {
  224 + zoneTF.setText(QiniuApplication.getConfigBean().getZone(newValue));
  225 + searchTF.clear();
  226 + String url = QiniuApplication.getConfigBean().getUrl(newValue);
  227 + if (Checker.isHyperLink(url)) {
  228 + domainTF.setText(url);
  229 + } else {
  230 + domainTF.setText(QiniuValueConsts.DOMAIN_CONFIG_ERROR);
  231 + }
  232 + ThreadPool.executor.submit(() -> {
  233 + if (SdkConfigurer.configUploadEnv(QiniuApplication.getConfigBean().getZone(newValue), newValue)) {
  234 + // 加载文件列表
  235 + mapResourceData();
  236 + // 刷新流量带宽统计
  237 + dateChange();
  238 + }
  239 + });
  240 + });
  241 + // 初始化统计单位选择框
  242 + fluxUnitCB.getItems().addAll("KB", "MB", "GB", "TB");
  243 + fluxUnitCB.setValue("KB");
  244 + fluxUnitCB.getSelectionModel().selectedItemProperty().addListener((obs, o, n) -> drawChart(true, false));
  245 + bandwidthUnitCB.getItems().addAll(fluxUnitCB.getItems());
  246 + bandwidthUnitCB.setValue("KB");
  247 + bandwidthUnitCB.getSelectionModel().selectedItemProperty().addListener((obs, o, n) -> drawChart(false, true));
  248 + }
  249 +
  250 + /**
  251 + * 开始拖曳文件
  252 + */
  253 + public void dragFileOver(DragEvent event) {
  254 + event.acceptTransferModes(TransferMode.ANY);
  255 + }
  256 +
  257 + /**
  258 + * 拖曳文件松开鼠标
  259 + */
  260 + public void dragFileDropped(DragEvent event) {
  261 + appendFile(event.getDragboard().getFiles());
  262 + }
  263 +
  264 + /**
  265 + * 开始日期或结束日期改变,刷新流量、带宽统计
  266 + */
  267 + public void dateChange() {
  268 + drawChart(true, true);
  269 + }
  270 +
  271 + /**
  272 + * 绘制数据统计图表
  273 + */
  274 + private void drawChart(boolean isFluxUnitChange, boolean isBandwidthUnitChange) {
  275 + Date localStartDate = Formatter.localDateToDate(startDP.getValue());
  276 + Date localEndDate = Formatter.localDateToDate(endDP.getValue());
  277 + // 将本地日期装换成字符串
  278 + String fromDate = Formatter.dateToString(localStartDate);
  279 + String toDate = Formatter.dateToString(localEndDate);
  280 + // 获取开始日期和结束日期的时间差
  281 + long timeSpan = localEndDate.getTime() - localStartDate.getTime();
  282 + if (Checker.isNotEmpty(domainTF.getText()) && timeSpan >= 0 && timeSpan <= QiniuValueConsts.DATE_SPAN_OF_THIRTY_ONE) {
  283 + Platform.runLater(() -> {
  284 + String[] domains = {domainTF.getText()};
  285 + if (isFluxUnitChange) {
  286 + // 获取流量数据
  287 + String fluxUnit = fluxUnitCB.getValue();
  288 + fluxAC.getData().clear();
  289 + fluxAC.getData().add(service.getBucketFlux(domains, fromDate, toDate, fluxUnit));
  290 + }
  291 + if (isBandwidthUnitChange) {
  292 + // 获取带宽数据
  293 + String bandUnit = bandwidthUnitCB.getValue();
  294 + bandwidthAC.getData().clear();
  295 + bandwidthAC.getData().add(service.getBucketBandwidth(domains, fromDate, toDate, bandUnit));
  296 + }
  297 + });
  298 + }
  299 + }
  300 +
  301 + /**
  302 + * 下载日志
  303 + */
  304 + public void downloadCdnLog() {
  305 + String date = DialogUtils.showInputDialog(null, QiniuValueConsts.INPUT_LOG_DATE,
  306 + Formatter.dateToString(new Date()));
  307 + service.downloadCdnLog(date);
  308 + }
  309 +
  310 + /**
  311 + * 刷新文件
  312 + */
  313 + public void refreshFile() {
  314 + service.refreshFile(resTV.getSelectionModel().getSelectedItems(), domainTF.getText());
  315 + }
  316 +
  317 + /**
  318 + * 用浏览器打开文件
  319 + */
  320 + public void openFile() {
  321 + ObservableList<FileBean> selectedItems = resTV.getSelectionModel().getSelectedItems();
  322 + if (Checker.isNotEmpty(selectedItems)) {
  323 + String filename = selectedItems.get(0).getName();
  324 + QiniuUtils.openLink(QiniuUtils.buildUrl(filename, domainTF.getText()));
  325 + }
  326 + }
  327 +
  328 + /**
  329 + * 私有下载
  330 + */
  331 + public void privateDownload() {
  332 + download(DownloadWay.PRIVATE);
  333 + }
  334 +
  335 + /**
  336 + * 下载文件
  337 + */
  338 + private void download(DownloadWay way) {
  339 + ObservableList<FileBean> selectedItems = resTV.getSelectionModel().getSelectedItems();
  340 + if (Checker.isNotEmpty(selectedItems)) {
  341 + if (way == DownloadWay.PUBLIC) {
  342 + selectedItems.forEach(bean -> service.publicDownload(bean.getName(), domainTF.getText()));
  343 + } else {
  344 + selectedItems.forEach(bean -> service.privateDownload(bean.getName(), domainTF.getText()));
  345 + }
  346 + }
  347 + }
  348 +
  349 + /**
  350 + * 公有下载
  351 + */
  352 + public void publicDownload() {
  353 + download(DownloadWay.PUBLIC);
  354 + }
  355 +
  356 + /**
  357 + * 更新镜像源
  358 + */
  359 + public void updateFile() {
  360 + ObservableList<FileBean> selectedItems = resTV.getSelectionModel().getSelectedItems();
  361 + if (Checker.isNotEmpty(selectedItems)) {
  362 + selectedItems.forEach(bean -> service.updateFile(bucketCB.getValue(), bean.getName()));
  363 + }
  364 + }
  365 +
  366 + /**
  367 + * 设置文件生存时间
  368 + */
  369 + public void setLife() {
  370 + ObservableList<FileBean> selectedItems = resTV.getSelectionModel().getSelectedItems();
  371 + if (Checker.isNotEmpty(selectedItems)) {
  372 + // 弹出输入框
  373 + String fileLife = DialogUtils.showInputDialog(null, QiniuValueConsts.FILE_LIFE,
  374 + QiniuValueConsts.DEFAULT_FILE_LIFE);
  375 + if (Checker.isNumber(fileLife)) {
  376 + int life = Formatter.stringToInt(fileLife);
  377 + selectedItems.forEach(bean -> service.setFileLife(bucketCB.getValue(), bean.getName(), life));
  378 + }
  379 + }
  380 + }
  381 +
  382 + /**
  383 + * 显示移动或复制文件的弹窗
  384 + */
  385 + public void showFileMovableDialog() {
  386 + ObservableList<FileBean> selectedItems = resTV.getSelectionModel().getSelectedItems();
  387 + if (Checker.isEmpty(selectedItems)) {
  388 + // 没有选择文件,结束方法
  389 + return;
  390 + }
  391 + Pair<SdkManager.FileAction, String[]> resultPair;
  392 + String bucket = bucketCB.getValue();
  393 + if (selectedItems.size() > 1) {
  394 + resultPair = dialog.showFileDialog(bucket, "", false);
  395 + } else {
  396 + resultPair = dialog.showFileDialog(bucket, selectedItems.get(0).getName(), true);
  397 + }
  398 + if (Checker.isNotNull(resultPair)) {
  399 + boolean useNewKey = Checker.isNotEmpty(resultPair.getValue()[1]);
  400 + ObservableList<FileBean> fileBeans = resTV.getItems();
  401 + for (FileBean fileBean : selectedItems) {
  402 + String fromBucket = bucketCB.getValue();
  403 + String toBucket = resultPair.getValue()[0];
  404 + String name = useNewKey ? resultPair.getValue()[1] : fileBean.getName();
  405 + boolean isSuccess = service.moveOrCopyFile(fromBucket, fileBean.getName(), toBucket, name,
  406 + resultPair.getKey());
  407 + if (resultPair.getKey() == SdkManager.FileAction.MOVE && isSuccess) {
  408 + boolean isInSearch = Checker.isNotEmpty(searchTF.getText());
  409 + if (fromBucket.equals(toBucket)) {
  410 + // 更新文件名
  411 + fileBean.setName(name);
  412 + if (isInSearch) {
  413 + fileBeans.get(fileBeans.indexOf(fileBean)).setName(name);
  414 + }
  415 + } else {
  416 + // 删除数据源
  417 + fileBeans.remove(fileBean);
  418 + dataLength--;
  419 + dataSize -= Formatter.sizeToLong(fileBean.getSize());
  420 + if (isInSearch) {
  421 + fileBeans.remove(fileBean);
  422 + }
  423 + }
  424 + }
  425 + }
  426 + countBucket();
  427 + }
  428 + }
  429 +
  430 + /**
  431 + * 删除文件
  432 + */
  433 + public void deleteFile() {
  434 + service.deleteFile(resTV.getSelectionModel().getSelectedItems(), bucketCB.getValue());
  435 + }
  436 +
  437 + /**
  438 + * 复制链接
  439 + */
  440 + public void copyLink() {
  441 + ObservableList<FileBean> fileBeans = resTV.getSelectionModel().getSelectedItems();
  442 + if (Checker.isNotEmpty(fileBeans)) {
  443 + // 只复制选中的第一个文件的链接
  444 + Utils.copyToClipboard(QiniuUtils.buildUrl(fileBeans.get(0).getName(), domainTF.getText()));
  445 + }
  446 + }
  447 +
  448 + /**
  449 + * 搜索资源文件,忽略大小写
  450 + */
  451 + public void searchFile() {
  452 + ArrayList<FileBean> files = new ArrayList<>();
  453 + String search = Checker.checkNull(searchTF.getText());
  454 + dataLength = 0;
  455 + dataSize = 0;
  456 + // 正则匹配查询
  457 + Pattern pattern = Pattern.compile(search, Pattern.CASE_INSENSITIVE);
  458 + for (FileBean file : resData) {
  459 + if (pattern.matcher(file.getName()).find()) {
  460 + files.add(file);
  461 + dataLength++;
  462 + dataSize += Formatter.sizeToLong(file.getSize());
  463 + }
  464 + }
  465 + countBucket();
  466 + resTV.setItems(FXCollections.observableArrayList(files));
  467 + }
  468 +
  469 + /**
  470 + * 统计空间文件的数量以及大小
  471 + */
  472 + public void countBucket() {
  473 + lengthLabel.setText(Formatter.customFormatDecimal(dataLength, ",###") + " 个文件");
  474 + sizeLabel.setText(Formatter.formatSize(dataSize));
  475 + }
  476 +
  477 + /**
  478 + * 刷新资源列表
  479 + */
  480 + public void refreshResourceData() {
  481 + mapResourceData();
  482 + DialogUtils.showInformation(QiniuValueConsts.REFRESH_SUCCESS);
  483 + }
  484 +
  485 + /**
  486 + * 将从存储空间获取的文件列表映射到表中
  487 + */
  488 + private void mapResourceData() {
  489 + ThreadPool.executor.submit(() -> {
  490 + // 列出资源文件
  491 + service.listFile();
  492 + Platform.runLater(() -> {
  493 + resTV.setItems(resData);
  494 + countBucket();
  495 + });
  496 + });
  497 +
  498 + }
  499 +
  500 + /**
  501 + * 添加桶至下拉框
  502 + */
  503 + public void appendBucket(String bucket) {
  504 + if (!bucketCB.getItems().contains(bucket)) {
  505 + bucketCB.getItems().add(bucket);
  506 + }
  507 + }
  508 +
  509 + /**
  510 + * 保存文件的上传状态
  511 + */
  512 + public void saveUploadStatus() {
  513 + FileChooser chooser = new FileChooser();
  514 + chooser.setTitle(QiniuValueConsts.FILE_CHOOSER_TITLE);
  515 + chooser.setInitialDirectory(new File(Utils.getCurrentWorkDir()));
  516 + File file = chooser.showSaveDialog(QiniuApplication.getStage());
  517 + QiniuUtils.saveFile(file, uploadStatusTA.getText());
  518 + }
  519 +
  520 + /**
  521 + * 复制文件上传状态至剪贴板
  522 + */
  523 + public void copyUploadStatus() {
  524 + Utils.copyToClipboard(uploadStatusTA.getText());
  525 + }
  526 +
  527 + /**
  528 + * 清空文件的上传状态
  529 + */
  530 + public void clearUploadStatus() {
  531 + uploadStatusTA.clear();
  532 + }
  533 +
  534 + /**
  535 + * 显示选择文件的弹窗
  536 + */
  537 + public void showOpenFileDialog() {
  538 + FileChooser chooser = new FileChooser();
  539 + chooser.setTitle(QiniuValueConsts.FILE_CHOOSER_TITLE);
  540 + chooser.setInitialDirectory(new File(Utils.getCurrentWorkDir()));
  541 + appendFile(chooser.showOpenMultipleDialog(QiniuApplication.getStage()));
  542 + }
  543 +
  544 + /**
  545 + * 添加上传的文件,支持拖曳文件夹
  546 + */
  547 + private void appendFile(List<File> files) {
  548 + if (Checker.isNotEmpty(files)) {
  549 + File[] fileArray = new File[files.size()];
  550 + appendFile(files.toArray(fileArray), false);
  551 + }
  552 + }
  553 +
  554 + /**
  555 + * 添加上传的文件,支持拖曳文件夹
  556 + */
  557 + private void appendFile(File[] files, boolean isRecursive) {
  558 + if (Checker.isNotNull(files)) {
  559 + for (File file : files) {
  560 + if (file.isDirectory()) {
  561 + if (isRecursive) {
  562 + // 递归添加文件
  563 + if (recursiveCB.isSelected()) {
  564 + appendFile(file.listFiles(), true);
  565 + }
  566 + } else {
  567 + rootPath.add(file.getAbsolutePath());
  568 + appendFile(file.listFiles(), true);
  569 + }
  570 + } else if (!selectedFileTA.getText().contains(file.getAbsolutePath())) {
  571 + selectedFileTA.insertText(0, file.getAbsolutePath() + "\r\n");
  572 + }
  573 + }
  574 + }
  575 + }
  576 +
  577 + /**
  578 + * 上传选择的文件
  579 + */
  580 + public void uploadFile() {
  581 + if (Checker.isEmpty(zoneTF.getText()) || Checker.isEmpty(selectedFileTA.getText())) {
  582 + // 没有选择存储空间或文件,不能上传文件
  583 + DialogUtils.showWarning(QiniuValueConsts.NEED_CHOOSE_BUCKET_OR_FILE);
  584 + return;
  585 + }
  586 + // 新建一个线程上传文件的线程
  587 + ThreadPool.executor.submit(() -> {
  588 + Platform.runLater(() -> uploadStatusTA.insertText(0, QiniuValueConsts.CONFIG_UPLOAD_ENVIRONMENT));
  589 + String bucket = bucketCB.getValue();
  590 + // 默认不指定KEY的情况下,以文件内容的哈希值作为文件名
  591 + String key = Checker.checkNull(prefixCB.getValue());
  592 + String[] paths = selectedFileTA.getText().split("\n");
  593 + // 去掉\r\n的长度
  594 + int endIndex = QiniuValueConsts.UPLOADING.length() - 2;
  595 + Platform.runLater(() -> uploadStatusTA.deleteText(0,
  596 + QiniuValueConsts.CONFIG_UPLOAD_ENVIRONMENT.length() - 1));
  597 + // 总文件数
  598 + for (String path : paths) {
  599 + if (Checker.isNotEmpty(path)) {
  600 + Platform.runLater(() -> uploadStatusTA.insertText(0, QiniuValueConsts.UPLOADING));
  601 + String filename = "";
  602 + String url = "http://" + QiniuApplication.getConfigBean().getUrl(bucket) + "/";
  603 + File file = new File(path);
  604 + try {
  605 + // 判断文件是否存在
  606 + if (file.exists()) {
  607 + // 保持文件相对父文件夹的路径
  608 + if (keepPathCB.isSelected() && Checker.isNotEmpty(rootPath)) {
  609 + for (String root : rootPath) {
  610 + if (file.getAbsolutePath().startsWith(root)) {
  611 + String postKey = root.substring(root.lastIndexOf(File.separator) + 1);
  612 + filename = key + postKey + file.getAbsolutePath().substring(root.length());
  613 + break;
  614 + }
  615 + }
  616 + }
  617 + if (Checker.isEmpty(filename)) {
  618 + filename = key + file.getName();
  619 + }
  620 + service.uploadFile(bucket, path, filename);
  621 + String now = DateUtil.formatDate(new Date());
  622 + status = StrUtil.format(UPLOAD_STATUS_TEMPLATE, now, url, filename, path);
  623 + } else if (Checker.isHyperLink(path)) {
  624 + // 抓取网络文件到空间中
  625 + filename = key + QiniuUtils.getFileName(path);
  626 + SdkConfigurer.getBucketManager().fetch(path, bucket, filename);
  627 + String now = DateUtil.formatDate(new Date());
  628 + status = StrUtil.format(UPLOAD_STATUS_TEMPLATE, now, url, filename, path);
  629 + } else {
  630 + // 文件不存在
  631 + status = DateUtil.formatDate(new Date()) + "\tfailed\t" + path;
  632 + }
  633 + } catch (QiniuException e) {
  634 + status = DateUtil.formatDate(new Date()) + "\terror\t" + path;
  635 + Platform.runLater(() -> DialogUtils.showException(QiniuValueConsts.UPLOAD_ERROR, e));
  636 + }
  637 + Platform.runLater(() -> {
  638 + uploadStatusTA.deleteText(0, endIndex);
  639 + uploadStatusTA.insertText(0, status);
  640 + });
  641 + }
  642 + Platform.runLater(() -> selectedFileTA.deleteText(0, path.length() + (paths.length > 1 ? 1 : 0)));
  643 + }
  644 + rootPath.clear();
  645 + Platform.runLater(() -> {
  646 + // 将光标移到最前面
  647 + uploadStatusTA.positionCaret(0);
  648 + // 清空待上传的文件列表
  649 + selectedFileTA.clear();
  650 + });
  651 + mapResourceData();
  652 + // 添加文件前缀到配置文件
  653 + savePrefix(key);
  654 + });
  655 + }
  656 +
  657 + /**
  658 + * 保存前缀
  659 + */
  660 + private void savePrefix(String key) {
  661 + if (Checker.isNotEmpty(key) && !QiniuApplication.getConfigBean().getPrefixes().contains(key)) {
  662 + Platform.runLater(() -> prefixCB.getItems().add(key));
  663 + QiniuApplication.getConfigBean().getPrefixes().add(key);
  664 + ConfigUtils.writeConfig();
  665 + }
  666 + }
  667 +
  668 + /**
  669 + * 打开配置文件
  670 + */
  671 + public void openConfigFile() {
  672 + try {
  673 + Desktop.getDesktop().open(new File(QiniuValueConsts.CONFIG_PATH));
  674 + // 用户触发是否重载配置文件
  675 + Optional<ButtonType> result = DialogUtils.showConfirmation(QiniuValueConsts.RELOAD_CONFIG);
  676 + if (result.isPresent() && result.get() == ButtonType.OK) {
  677 + // 重新载入配置文件
  678 + bucketCB.getItems().clear();
  679 + prefixCB.getItems().clear();
  680 + ConfigUtils.loadConfig();
  681 + }
  682 + } catch (Exception e) {
  683 + DialogUtils.showException(QiniuValueConsts.OPEN_FILE_ERROR, e);
  684 + }
  685 + }
  686 +
  687 + /**
  688 + * 显示重置密钥的弹窗
  689 + */
  690 + public void showKeyDialog() {
  691 + boolean ok = dialog.showKeyDialog();
  692 + if (ok && Checker.isNotEmpty(zoneTF.getText())) {
  693 + // 配置新的环境
  694 + SdkConfigurer.configUploadEnv(zoneTF.getText(), bucketCB.getValue());
  695 + }
  696 + }
  697 +
  698 + /**
  699 + * 显示添加存储空间的弹窗
  700 + */
  701 + public void showBucketDialog() {
  702 + dialog.showBucketDialog();
  703 + }
  704 +
  705 + public enum DownloadWay {
  706 + // 下载的方式,包括私有和公有
  707 + PRIVATE, PUBLIC
  708 + }
  709 +}
  1 +package org.code4everything.qiniu.model;
  2 +
  3 +import lombok.AllArgsConstructor;
  4 +import lombok.Data;
  5 +import lombok.NoArgsConstructor;
  6 +
  7 +import java.io.Serializable;
  8 +
  9 +/**
  10 + * 桶信息
  11 + *
  12 + * @author pantao
  13 + * @since 2018/11/13
  14 + */
  15 +@Data
  16 +@AllArgsConstructor
  17 +@NoArgsConstructor
  18 +public class BucketBean implements Serializable {
  19 +
  20 + private String bucket;
  21 +
  22 + private String zone;
  23 +
  24 + private String url;
  25 +}
  1 +package org.code4everything.qiniu.model;
  2 +
  3 +import cn.hutool.core.collection.CollectionUtil;
  4 +import lombok.AllArgsConstructor;
  5 +import lombok.Data;
  6 +import lombok.NoArgsConstructor;
  7 +
  8 +import java.io.Serializable;
  9 +import java.util.ArrayList;
  10 +import java.util.Objects;
  11 +
  12 +/**
  13 + * 应用配置信息
  14 + *
  15 + * @author pantao
  16 + * @since 2018/11/12
  17 + **/
  18 +@Data
  19 +@NoArgsConstructor
  20 +@AllArgsConstructor
  21 +public class ConfigBean implements Serializable {
  22 +
  23 + private String accessKey;
  24 +
  25 + private String secretKey;
  26 +
  27 + private ArrayList<BucketBean> buckets;
  28 +
  29 + private ArrayList<String> prefixes;
  30 +
  31 + private String storagePath;
  32 +
  33 + public String getBucket(String bucket) {
  34 + BucketBean bucketBean = getBucketBean(bucket);
  35 + return Objects.isNull(bucketBean) ? "" : bucketBean.getBucket();
  36 + }
  37 +
  38 + public String getUrl(String bucket) {
  39 + BucketBean bucketBean = getBucketBean(bucket);
  40 + return Objects.isNull(bucketBean) ? "" : bucketBean.getUrl();
  41 + }
  42 +
  43 + public String getZone(String bucket) {
  44 + BucketBean bucketBean = getBucketBean(bucket);
  45 + return Objects.isNull(bucketBean) ? "" : bucketBean.getZone();
  46 + }
  47 +
  48 + public BucketBean getBucketBean(String bucket) {
  49 + if (CollectionUtil.isNotEmpty(buckets)) {
  50 + for (BucketBean bean : buckets) {
  51 + if (Objects.equals(bean.getBucket(), bucket)) {
  52 + return bean;
  53 + }
  54 + }
  55 + }
  56 + return null;
  57 + }
  58 +}
  1 +package org.code4everything.qiniu.model;
  2 +
  3 +import lombok.AllArgsConstructor;
  4 +import lombok.Data;
  5 +import lombok.NoArgsConstructor;
  6 +
  7 +/**
  8 + * 文件信息
  9 + *
  10 + * @author pantao
  11 + */
  12 +@Data
  13 +@AllArgsConstructor
  14 +@NoArgsConstructor
  15 +public class FileBean {
  16 +
  17 + private String name;
  18 +
  19 + private String type;
  20 +
  21 + private String size;
  22 +
  23 + private String time;
  24 +}
  1 +package org.code4everything.qiniu.service;
  2 +
  3 +import com.qiniu.cdn.CdnResult;
  4 +import com.qiniu.common.QiniuException;
  5 +import com.qiniu.storage.BucketManager;
  6 +import com.qiniu.storage.model.BatchStatus;
  7 +import com.qiniu.storage.model.FileInfo;
  8 +import com.zhazhapan.util.Checker;
  9 +import com.zhazhapan.util.Formatter;
  10 +import javafx.application.Platform;
  11 +import javafx.collections.FXCollections;
  12 +import javafx.collections.ObservableList;
  13 +import javafx.scene.chart.XYChart;
  14 +import org.apache.log4j.Logger;
  15 +import org.code4everything.qiniu.QiniuApplication;
  16 +import org.code4everything.qiniu.api.SdkConfigurer;
  17 +import org.code4everything.qiniu.api.SdkManager;
  18 +import org.code4everything.qiniu.constant.QiniuValueConsts;
  19 +import org.code4everything.qiniu.controller.MainController;
  20 +import org.code4everything.qiniu.model.FileBean;
  21 +import org.code4everything.qiniu.util.DialogUtils;
  22 +import org.code4everything.qiniu.util.QiniuUtils;
  23 +
  24 +import java.util.ArrayList;
  25 +import java.util.Map;
  26 +
  27 +/**
  28 + * 七牛服务类
  29 + *
  30 + * @author pantao
  31 + * @since 2018/11/13
  32 + */
  33 +public class QiniuService {
  34 +
  35 + private static final Logger LOGGER = Logger.getLogger(QiniuService.class);
  36 +
  37 + private final SdkManager sdkManager = new SdkManager();
  38 +
  39 + /**
  40 + * 上传文件
  41 + */
  42 + public void uploadFile(String bucket, String key, String filename) throws QiniuException {
  43 + String upToken = SdkConfigurer.getAuth().uploadToken(bucket, filename);
  44 + SdkConfigurer.getUploadManager().put(key, filename, upToken);
  45 + }
  46 +
  47 + /**
  48 + * 获取空间文件列表
  49 + */
  50 + public void listFile() {
  51 + MainController main = MainController.getInstance();
  52 + // 列举空间文件列表
  53 + BucketManager.FileListIterator iterator = sdkManager.getFileListIterator(main.bucketCB.getValue());
  54 + ArrayList<FileBean> files = new ArrayList<>();
  55 + main.setDataLength(0);
  56 + main.setDataSize(0);
  57 + // 处理结果
  58 + while (iterator.hasNext()) {
  59 + FileInfo[] items = iterator.next();
  60 + for (FileInfo item : items) {
  61 + main.setDataLength(main.getDataLength() + 1);
  62 + main.setDataSize(main.getDataSize() + item.fsize);
  63 + // 将七牛的时间单位(100纳秒)转换成毫秒,然后转换成时间
  64 + String time = Formatter.timeStampToString(item.putTime / 10000);
  65 + String size = Formatter.formatSize(item.fsize);
  66 + FileBean file = new FileBean(item.key, item.mimeType, size, time);
  67 + files.add(file);
  68 + }
  69 + }
  70 + main.setResData(FXCollections.observableArrayList(files));
  71 + }
  72 +
  73 + /**
  74 + * 批量删除文件,单次批量请求的文件数量不得超过1000
  75 + */
  76 + public void deleteFile(ObservableList<FileBean> fileBeans, String bucket) {
  77 + if (Checker.isNotEmpty(fileBeans) && QiniuUtils.checkNet()) {
  78 + // 生成待删除的文件列表
  79 + String[] files = new String[fileBeans.size()];
  80 + ArrayList<FileBean> selectedFiles = new ArrayList<>();
  81 + int i = 0;
  82 + for (FileBean fileBean : fileBeans) {
  83 + files[i++] = fileBean.getName();
  84 + selectedFiles.add(fileBean);
  85 + }
  86 + try {
  87 + BatchStatus[] batchStatusList = sdkManager.batchDelete(bucket, files);
  88 + MainController main = MainController.getInstance();
  89 + // 文件列表是否为搜索后结果
  90 + boolean isInSearch = Checker.isNotEmpty(main.searchTF.getText());
  91 + ObservableList<FileBean> currentRes = main.resTV.getItems();
  92 + // 更新界面数据
  93 + for (i = 0; i < files.length; i++) {
  94 + BatchStatus status = batchStatusList[i];
  95 + String file = files[i];
  96 + if (status.code == 200) {
  97 + main.getResData().remove(selectedFiles.get(i));
  98 + main.setDataLength(main.getDataLength() - 1);
  99 + main.setDataSize(main.getDataSize() - Formatter.sizeToLong(selectedFiles.get(i).getSize()));
  100 + if (isInSearch) {
  101 + currentRes.remove(selectedFiles.get(i));
  102 + }
  103 + } else {
  104 + LOGGER.error("delete " + file + " failed, message -> " + status.data.error);
  105 + DialogUtils.showError("删除文件:" + file + " 失败");
  106 + }
  107 + }
  108 + } catch (QiniuException e) {
  109 + DialogUtils.showException(QiniuValueConsts.DELETE_ERROR, e);
  110 + }
  111 + MainController.getInstance().countBucket();
  112 + }
  113 + }
  114 +
  115 + /**
  116 + * 修改文件类型
  117 + */
  118 + public boolean changeType(String fileName, String newType, String bucket) {
  119 + boolean result = true;
  120 + try {
  121 + sdkManager.changeMime(bucket, fileName, newType);
  122 + } catch (QiniuException e) {
  123 + DialogUtils.showException(QiniuValueConsts.CHANGE_FILE_TYPE_ERROR, e);
  124 + result = false;
  125 + }
  126 + return result;
  127 + }
  128 +
  129 + /**
  130 + * 重命名文件
  131 + */
  132 + public boolean renameFile(String bucket, String oldName, String newName) {
  133 + return moveFile(bucket, oldName, bucket, newName);
  134 + }
  135 +
  136 + /**
  137 + * 移动文件
  138 + */
  139 + private boolean moveFile(String srcBucket, String fromKey, String destBucket, String toKey) {
  140 + return moveOrCopyFile(srcBucket, fromKey, destBucket, toKey, SdkManager.FileAction.MOVE);
  141 + }
  142 +
  143 + /**
  144 + * 移动或复制文件
  145 + */
  146 + public boolean moveOrCopyFile(String srcBucket, String srcKey, String destBucket, String destKey,
  147 + SdkManager.FileAction fileAction) {
  148 + boolean result = true;
  149 + try {
  150 + sdkManager.moveOrCopyFile(srcBucket, srcKey, destBucket, destKey, fileAction);
  151 + } catch (QiniuException e) {
  152 + LOGGER.error("move file failed, message -> " + e.getMessage());
  153 + DialogUtils.showException(QiniuValueConsts.MOVE_OR_RENAME_ERROR, e);
  154 + result = false;
  155 + }
  156 + return result;
  157 + }
  158 +
  159 + /**
  160 + * 设置文件生存时间
  161 + */
  162 + public void setFileLife(String bucket, String key, int days) {
  163 + try {
  164 + sdkManager.deleteAfterDays(bucket, key, days);
  165 + LOGGER.info("set file life success");
  166 + } catch (QiniuException e) {
  167 + LOGGER.error("set file life error, message -> " + e.getMessage());
  168 + DialogUtils.showException(QiniuValueConsts.MOVE_OR_RENAME_ERROR, e);
  169 + }
  170 + }
  171 +
  172 + /**
  173 + * 更新镜像源
  174 + */
  175 + public void updateFile(String bucket, String key) {
  176 + try {
  177 + sdkManager.prefetch(bucket, key);
  178 + LOGGER.info("prefetch files success");
  179 + } catch (QiniuException e) {
  180 + LOGGER.error("prefetch files error, message -> " + e.getMessage());
  181 + DialogUtils.showException(QiniuValueConsts.UPDATE_ERROR, e);
  182 + }
  183 + }
  184 +
  185 + /**
  186 + * 公有下载
  187 + */
  188 + public void publicDownload(String fileName, String domain) {
  189 + QiniuUtils.download(QiniuUtils.buildUrl(fileName, domain));
  190 + }
  191 +
  192 + /**
  193 + * 私有下载
  194 + */
  195 + public void privateDownload(String fileName, String domain) {
  196 + QiniuUtils.download(sdkManager.getPrivateUrl(QiniuUtils.buildUrl(fileName, domain)));
  197 + }
  198 +
  199 + /**
  200 + * 刷新文件
  201 + */
  202 + public void refreshFile(ObservableList<FileBean> fileBeans, String domain) {
  203 + if (Checker.isNotEmpty(fileBeans)) {
  204 + String[] files = new String[fileBeans.size()];
  205 + int i = 0;
  206 + // 获取公有链接
  207 + for (FileBean fileBean : fileBeans) {
  208 + files[i++] = QiniuUtils.buildUrl(fileBean.getName(), domain);
  209 + }
  210 + try {
  211 + // 刷新文件
  212 + sdkManager.refreshFile(files);
  213 + } catch (QiniuException e) {
  214 + LOGGER.error("refresh files error, message -> " + e.getMessage());
  215 + DialogUtils.showException(e);
  216 + }
  217 + }
  218 + }
  219 +
  220 + /**
  221 + * 日志下载
  222 + */
  223 + public void downloadCdnLog(String logDate) {
  224 + if (Checker.isNotEmpty(QiniuApplication.getConfigBean().getBuckets()) && Checker.isDate(logDate)) {
  225 + // 转换域名成数组格式
  226 + String[] domains = new String[QiniuApplication.getConfigBean().getBuckets().size()];
  227 + for (int i = 0; i < QiniuApplication.getConfigBean().getBuckets().size(); i++) {
  228 + domains[i] = QiniuApplication.getConfigBean().getBuckets().get(i).getUrl();
  229 + }
  230 + Map<String, CdnResult.LogData[]> cdnLog = null;
  231 + try {
  232 + cdnLog = sdkManager.listCdnLog(domains, logDate);
  233 + } catch (QiniuException e) {
  234 + DialogUtils.showException(e);
  235 + }
  236 + if (Checker.isNotEmpty(cdnLog)) {
  237 + // 下载日志
  238 + for (Map.Entry<String, CdnResult.LogData[]> logs : cdnLog.entrySet()) {
  239 + for (CdnResult.LogData log : logs.getValue()) {
  240 + QiniuUtils.download(log.url);
  241 + }
  242 + }
  243 + }
  244 + }
  245 + }
  246 +
  247 +
  248 + /**
  249 + * 获取空间带宽统计,使用自定义单位
  250 + */
  251 + public XYChart.Series<String, Long> getBucketBandwidth(String[] domains, String startDate, String endDate,
  252 + String unit) {
  253 + // 获取带宽数据
  254 + CdnResult.BandwidthResult bandwidthResult = null;
  255 + try {
  256 + bandwidthResult = sdkManager.getBandwidthData(domains, startDate, endDate);
  257 + } catch (QiniuException e) {
  258 + Platform.runLater(() -> DialogUtils.showException(QiniuValueConsts.BUCKET_BAND_ERROR, e));
  259 + }
  260 + // 设置图表
  261 + XYChart.Series<String, Long> series = new XYChart.Series<>();
  262 + series.setName(QiniuValueConsts.BUCKET_BANDWIDTH_COUNT.replaceAll("[A-Z]+", unit));
  263 + // 格式化数据
  264 + if (Checker.isNotNull(bandwidthResult) && Checker.isNotEmpty(bandwidthResult.data)) {
  265 + long unitSize = Formatter.sizeToLong("1 " + unit);
  266 + for (Map.Entry<String, CdnResult.BandwidthData> bandwidth : bandwidthResult.data.entrySet()) {
  267 + CdnResult.BandwidthData bandwidthData = bandwidth.getValue();
  268 + if (Checker.isNotNull(bandwidthData)) {
  269 + setSeries(bandwidthResult.time, bandwidthData.china, bandwidthData.oversea, series, unitSize);
  270 + }
  271 + }
  272 + }
  273 + return series;
  274 + }
  275 +
  276 + /**
  277 + * 获取空间的流量统计,使用自定义单位
  278 + */
  279 + public XYChart.Series<String, Long> getBucketFlux(String[] domains, String startDate, String endDate, String unit) {
  280 + // 获取流量数据
  281 + CdnResult.FluxResult fluxResult = null;
  282 + try {
  283 + fluxResult = sdkManager.getFluxData(domains, startDate, endDate);
  284 + } catch (QiniuException e) {
  285 + Platform.runLater(() -> DialogUtils.showException(QiniuValueConsts.BUCKET_FLUX_ERROR, e));
  286 + }
  287 + // 设置图表
  288 + XYChart.Series<String, Long> series = new XYChart.Series<>();
  289 + series.setName(QiniuValueConsts.BUCKET_FLUX_COUNT.replaceAll("[A-Z]+", unit));
  290 + // 格式化数据
  291 + if (Checker.isNotNull(fluxResult) && Checker.isNotEmpty(fluxResult.data)) {
  292 + long unitSize = Formatter.sizeToLong("1 " + unit);
  293 + for (Map.Entry<String, CdnResult.FluxData> flux : fluxResult.data.entrySet()) {
  294 + CdnResult.FluxData fluxData = flux.getValue();
  295 + if (Checker.isNotNull(fluxData)) {
  296 + setSeries(fluxResult.time, fluxData.china, fluxData.oversea, series, unitSize);
  297 + }
  298 + }
  299 + }
  300 + return series;
  301 + }
  302 +
  303 + /**
  304 + * 处理带宽数据
  305 + */
  306 + private void setSeries(String[] times, Long[] china, Long[] oversea, XYChart.Series<String, Long> series,
  307 + long unit) {
  308 + int i = 0;
  309 + for (String time : times) {
  310 + long size = 0;
  311 + if (Checker.isNotEmpty(china)) {
  312 + size += china[i];
  313 + }
  314 + if (Checker.isNotEmpty(oversea)) {
  315 + size += oversea[i];
  316 + }
  317 + series.getData().add(new XYChart.Data<>(time.substring(5, 10), size / unit));
  318 + i++;
  319 + }
  320 + }
  321 +}
  1 +package org.code4everything.qiniu.util;
  2 +
  3 +import cn.hutool.core.collection.CollectionUtil;
  4 +import cn.hutool.core.io.FileUtil;
  5 +import cn.hutool.core.util.ObjectUtil;
  6 +import cn.hutool.core.util.StrUtil;
  7 +import com.alibaba.fastjson.JSONObject;
  8 +import javafx.application.Platform;
  9 +import org.code4everything.qiniu.QiniuApplication;
  10 +import org.code4everything.qiniu.api.SdkConfigurer;
  11 +import org.code4everything.qiniu.constant.QiniuValueConsts;
  12 +import org.code4everything.qiniu.controller.MainController;
  13 +import org.code4everything.qiniu.model.BucketBean;
  14 +import org.code4everything.qiniu.model.ConfigBean;
  15 +
  16 +import java.util.ArrayList;
  17 +
  18 +/**
  19 + * 配置文件工具类
  20 + *
  21 + * @author pantao
  22 + * @since 2018/11/12
  23 + **/
  24 +public class ConfigUtils {
  25 +
  26 + private static final String CONFIG_PATH = QiniuValueConsts.CONFIG_PATH;
  27 +
  28 + private ConfigUtils() {}
  29 +
  30 + /**
  31 + * 加载配置文件
  32 + */
  33 + public static void loadConfig() {
  34 + QiniuApplication.setConfigBean(new ConfigBean());
  35 + if (FileUtil.exist(CONFIG_PATH)) {
  36 + ConfigBean config = JSONObject.parseObject(FileUtil.readUtf8String(CONFIG_PATH), ConfigBean.class);
  37 + if (ObjectUtil.isNotNull(config)) {
  38 + QiniuApplication.setConfigBean(config);
  39 + if (StrUtil.isNotEmpty(config.getAccessKey()) && StrUtil.isNotEmpty(config.getSecretKey())) {
  40 + // 创建上传权限
  41 + SdkConfigurer.createAuth(config.getAccessKey(), config.getSecretKey());
  42 + }
  43 + MainController controller = MainController.getInstance();
  44 + if (CollectionUtil.isEmpty(config.getBuckets())) {
  45 + // 设置一个空的桶列表,防止出现空指针
  46 + config.setBuckets(new ArrayList<>());
  47 + } else {
  48 + Platform.runLater(() -> {
  49 + // 添加桶
  50 + config.getBuckets().forEach(bucket -> controller.appendBucket(bucket.getBucket()));
  51 + // 选中第一个桶
  52 + BucketBean bucket = config.getBuckets().get(0);
  53 + controller.bucketCB.setValue(bucket.getBucket());
  54 + controller.zoneTF.setText(bucket.getZone());
  55 + });
  56 + }
  57 + if (CollectionUtil.isEmpty(config.getPrefixes())) {
  58 + // 设置一个空的前缀列表,防止出现空指针
  59 + config.setPrefixes(new ArrayList<>());
  60 + } else {
  61 + // 添加前缀
  62 + Platform.runLater(() -> config.getPrefixes().forEach(prefix -> controller.prefixCB.getItems().add(prefix)));
  63 + }
  64 + return;
  65 + }
  66 + }
  67 + // 设置一个空的配置对象,防止出现空指针
  68 + ConfigBean configBean = new ConfigBean();
  69 + configBean.setPrefixes(new ArrayList<>());
  70 + configBean.setBuckets(new ArrayList<>());
  71 + QiniuApplication.setConfigBean(configBean);
  72 + writeConfig();
  73 + }
  74 +
  75 + /**
  76 + * 写配置文件
  77 + */
  78 + public static void writeConfig() {
  79 + FileUtil.writeUtf8String(JSONObject.toJSONString(QiniuApplication.getConfigBean(), true), CONFIG_PATH);
  80 + }
  81 +}
  1 +package org.code4everything.qiniu.util;
  2 +
  3 +import javafx.scene.control.Alert;
  4 +import javafx.scene.control.Alert.AlertType;
  5 +import javafx.scene.control.ButtonType;
  6 +import javafx.scene.control.TextArea;
  7 +import javafx.scene.control.TextInputDialog;
  8 +import javafx.scene.layout.GridPane;
  9 +import javafx.scene.layout.Priority;
  10 +import javafx.stage.Modality;
  11 +import javafx.stage.StageStyle;
  12 +import javafx.stage.Window;
  13 +import org.code4everything.qiniu.constant.QiniuValueConsts;
  14 +
  15 +import java.io.PrintWriter;
  16 +import java.io.StringWriter;
  17 +import java.util.Optional;
  18 +
  19 +/**
  20 + * 弹窗工具类
  21 + *
  22 + * @author pantao 对JavaFX对话框进行封装
  23 + */
  24 +public class DialogUtils {
  25 +
  26 + private DialogUtils() {}
  27 +
  28 + public static String showInputDialog(String header, String content, String defaultValue) {
  29 + TextInputDialog dialog = new TextInputDialog(defaultValue);
  30 + dialog.setTitle(QiniuValueConsts.MAIN_TITLE);
  31 + dialog.setHeaderText(header);
  32 + dialog.setContentText(content);
  33 + Optional<String> result = dialog.showAndWait();
  34 + return result.orElse(null);
  35 + }
  36 +
  37 + public static Optional<ButtonType> showInformation(String content) {
  38 + return showInformation(null, content);
  39 + }
  40 +
  41 + public static Optional<ButtonType> showInformation(String header, String content) {
  42 + return showAlert(header, content, AlertType.INFORMATION);
  43 + }
  44 +
  45 + public static Optional<ButtonType> showWarning(String content) {
  46 + return showWarning(null, content);
  47 + }
  48 +
  49 + public static Optional<ButtonType> showWarning(String header, String content) {
  50 + return showAlert(header, content, AlertType.WARNING);
  51 + }
  52 +
  53 + public static Optional<ButtonType> showError(String content) {
  54 + return showError(null, content);
  55 + }
  56 +
  57 + public static Optional<ButtonType> showError(String header, String content) {
  58 + return showAlert(header, content, AlertType.ERROR);
  59 + }
  60 +
  61 + public static Optional<ButtonType> showConfirmation(String content) {
  62 + return showConfirmation(null, content);
  63 + }
  64 +
  65 + public static Optional<ButtonType> showConfirmation(String header, String content) {
  66 + return showAlert(header, content, AlertType.CONFIRMATION);
  67 + }
  68 +
  69 + public static Optional<ButtonType> showException(Exception e) {
  70 + return showException(null, e);
  71 + }
  72 +
  73 + public static void showFatalError(String header, Exception e) {
  74 + showException(header, e);
  75 + System.exit(0);
  76 + }
  77 +
  78 + public static Optional<ButtonType> showException(String header, Exception e) {
  79 + // 获取一个警告对象
  80 + Alert alert = getAlert(header, "错误信息追踪:", AlertType.ERROR);
  81 + // 打印异常信息
  82 + StringWriter stringWriter = new StringWriter();
  83 + PrintWriter printWriter = new PrintWriter(stringWriter);
  84 + e.printStackTrace(printWriter);
  85 + String exception = stringWriter.toString();
  86 + // 显示异常信息
  87 + TextArea textArea = new TextArea(exception);
  88 + textArea.setEditable(false);
  89 + textArea.setWrapText(true);
  90 + // 设置弹窗容器
  91 + textArea.setMaxWidth(Double.MAX_VALUE);
  92 + textArea.setMaxHeight(Double.MAX_VALUE);
  93 + GridPane.setVgrow(textArea, Priority.ALWAYS);
  94 + GridPane.setHgrow(textArea, Priority.ALWAYS);
  95 + GridPane gridPane = new GridPane();
  96 + gridPane.setMaxWidth(Double.MAX_VALUE);
  97 + gridPane.add(textArea, 0, 0);
  98 + alert.getDialogPane().setExpandableContent(gridPane);
  99 + return alert.showAndWait();
  100 + }
  101 +
  102 + public static Optional<ButtonType> showAlert(String content) {
  103 + return showAlert(null, content);
  104 + }
  105 +
  106 + public static Optional<ButtonType> showAlert(String content, AlertType alertType) {
  107 + return showAlert(null, content, alertType);
  108 + }
  109 +
  110 + public static Optional<ButtonType> showAlert(String header, String content) {
  111 + return showAlert(header, content, AlertType.INFORMATION);
  112 + }
  113 +
  114 + public static Optional<ButtonType> showAlert(String header, String content, AlertType alertType) {
  115 + return showAlert(header, content, alertType, Modality.NONE, null, StageStyle.DECORATED);
  116 + }
  117 +
  118 + public static Optional<ButtonType> showAlert(String header, String content, AlertType alertType,
  119 + Modality modality, Window window, StageStyle style) {
  120 + return getAlert(header, content, alertType, modality, window, style).showAndWait();
  121 + }
  122 +
  123 + public static Alert getAlert(String header, String content, AlertType alertType) {
  124 + return getAlert(header, content, alertType, Modality.APPLICATION_MODAL, null, StageStyle.DECORATED);
  125 + }
  126 +
  127 + public static Alert getAlert(String header, String content, AlertType alertType, Modality modality, Window window
  128 + , StageStyle style) {
  129 + Alert alert = new Alert(alertType);
  130 + alert.setTitle(QiniuValueConsts.MAIN_TITLE);
  131 + alert.setHeaderText(header);
  132 + alert.setContentText(content);
  133 + alert.initModality(modality);
  134 + alert.initOwner(window);
  135 + alert.initStyle(style);
  136 + return alert;
  137 + }
  138 +}
  1 +package org.code4everything.qiniu.util;
  2 +
  3 +import com.zhazhapan.util.Checker;
  4 +import com.zhazhapan.util.dialog.Dialogs;
  5 +import javafx.application.Platform;
  6 +import javafx.scene.Node;
  7 +import javafx.scene.control.*;
  8 +import javafx.scene.layout.GridPane;
  9 +import javafx.stage.Modality;
  10 +import javafx.util.Pair;
  11 +import org.code4everything.qiniu.QiniuApplication;
  12 +import org.code4everything.qiniu.api.SdkConfigurer;
  13 +import org.code4everything.qiniu.api.SdkManager;
  14 +import org.code4everything.qiniu.constant.QiniuValueConsts;
  15 +import org.code4everything.qiniu.controller.MainController;
  16 +import org.code4everything.qiniu.model.BucketBean;
  17 +
  18 +import java.util.Optional;
  19 +
  20 +/**
  21 + * 应用弹窗
  22 + *
  23 + * @author pantao
  24 + * @since 2018/11/13
  25 + */
  26 +public class QiniuDialog {
  27 +
  28 + /**
  29 + * 显示移动文件的弹窗
  30 + */
  31 + public Pair<SdkManager.FileAction, String[]> showFileDialog(String bucket, String key, boolean setKey) {
  32 + MainController main = MainController.getInstance();
  33 + ButtonType ok = new ButtonType(QiniuValueConsts.OK, ButtonBar.ButtonData.OK_DONE);
  34 + Dialog<String[]> dialog = getDialog(ok);
  35 + // 文件名输入框
  36 + TextField keyTextField = new TextField();
  37 + keyTextField.setPrefWidth(300);
  38 + keyTextField.setPromptText(QiniuValueConsts.FILE_NAME);
  39 + keyTextField.setText(key);
  40 + // 空间选择框
  41 + ComboBox<String> bucketCombo = new ComboBox<>();
  42 + bucketCombo.getItems().addAll(main.bucketCB.getItems());
  43 + bucketCombo.setValue(bucket);
  44 + // 保存文件副本复选框
  45 + CheckBox copyAsCheckBox = new CheckBox(QiniuValueConsts.COPY_AS);
  46 + copyAsCheckBox.setSelected(true);
  47 + // 设置容器
  48 + GridPane grid = Dialogs.getGridPane();
  49 + grid.add(copyAsCheckBox, 0, 0, 2, 1);
  50 + grid.add(new Label(QiniuValueConsts.BUCKET_NAME), 0, 1);
  51 + grid.add(bucketCombo, 1, 1);
  52 + if (setKey) {
  53 + // 显示文件名输入框
  54 + grid.add(new Label(QiniuValueConsts.FILE_NAME), 0, 2);
  55 + grid.add(keyTextField, 1, 2);
  56 + Platform.runLater(keyTextField::requestFocus);
  57 + }
  58 + dialog.getDialogPane().setContent(grid);
  59 + // 数据转换器
  60 + dialog.setResultConverter(dialogButton -> {
  61 + if (dialogButton == ok) {
  62 + return new String[]{bucketCombo.getValue(), keyTextField.getText()};
  63 + }
  64 + return null;
  65 + });
  66 + // 读取数据
  67 + Optional<String[]> result = dialog.showAndWait();
  68 + if (result.isPresent()) {
  69 + bucket = bucketCombo.getValue();
  70 + key = keyTextField.getText();
  71 + SdkManager.FileAction action = copyAsCheckBox.isSelected() ? SdkManager.FileAction.COPY :
  72 + SdkManager.FileAction.MOVE;
  73 + return new Pair<>(action, new String[]{bucket, key});
  74 + } else {
  75 + return null;
  76 + }
  77 + }
  78 +
  79 + /**
  80 + * 显示输入密钥的对话框
  81 + *
  82 + * @return 返回用户是否点击确定按钮
  83 + */
  84 + public boolean showKeyDialog() {
  85 + ButtonType ok = new ButtonType(QiniuValueConsts.OK, ButtonBar.ButtonData.OK_DONE);
  86 + Dialog<String[]> dialog = getDialog(ok);
  87 + // 文本框
  88 + TextField ak = new TextField();
  89 + ak.setMinWidth(400);
  90 + ak.setPromptText("Access Key");
  91 + TextField sk = new TextField();
  92 + sk.setPromptText("Secret Key");
  93 + // 设置超链接
  94 + Hyperlink hyperlink = new Hyperlink("查看我的KEY:" + QiniuValueConsts.QINIU_KEY_URL);
  95 + hyperlink.setOnAction(event -> QiniuUtils.openLink(QiniuValueConsts.QINIU_KEY_URL));
  96 + // 设置容器
  97 + GridPane grid = Dialogs.getGridPane();
  98 + grid.add(hyperlink, 0, 0, 2, 1);
  99 + grid.add(new Label("Access Key:"), 0, 1);
  100 + grid.add(ak, 1, 1);
  101 + grid.add(new Label("Secret Key:"), 0, 2);
  102 + grid.add(sk, 1, 2);
  103 + Node okButton = dialog.getDialogPane().lookupButton(ok);
  104 + okButton.setDisable(true);
  105 + dialog.getDialogPane().setContent(grid);
  106 + Platform.runLater(ak::requestFocus);
  107 + // 监听文本框的输入状态
  108 + ak.textProperty().addListener((observable, oldValue, newValue) -> okButton.setDisable(newValue.trim().isEmpty() || sk.getText().trim().isEmpty()));
  109 + sk.textProperty().addListener((observable, oldValue, newValue) -> okButton.setDisable(newValue.trim().isEmpty() || ak.getText().trim().isEmpty()));
  110 + // 等待用户操作
  111 + Optional<String[]> result = dialog.showAndWait();
  112 + // 处理结果
  113 + if (result.isPresent() && Checker.isNotEmpty(ak.getText()) && Checker.isNotEmpty(sk.getText())) {
  114 + QiniuApplication.getConfigBean().setAccessKey(ak.getText());
  115 + QiniuApplication.getConfigBean().setSecretKey(sk.getText());
  116 + SdkConfigurer.createAuth(ak.getText(), sk.getText());
  117 + ConfigUtils.writeConfig();
  118 + return true;
  119 + }
  120 + return false;
  121 + }
  122 +
  123 + /**
  124 + * 显示添加桶的弹窗
  125 + */
  126 + public void showBucketDialog() {
  127 + ButtonType ok = new ButtonType(QiniuValueConsts.OK, ButtonBar.ButtonData.OK_DONE);
  128 + Dialog<String[]> dialog = getDialog(ok);
  129 + // 桶名称输入框
  130 + TextField bucket = new TextField();
  131 + bucket.setPromptText(QiniuValueConsts.BUCKET_NAME);
  132 + // 桶域名输入框
  133 + TextField url = new TextField();
  134 + url.setPromptText(QiniuValueConsts.BUCKET_URL);
  135 + // 桶区域输入框
  136 + ComboBox<String> zone = new ComboBox<>();
  137 + zone.getItems().addAll(QiniuValueConsts.BUCKET_NAME_ARRAY);
  138 + zone.setValue(QiniuValueConsts.BUCKET_NAME_ARRAY[0]);
  139 + // 设置容器
  140 + GridPane grid = Dialogs.getGridPane();
  141 + grid.add(new Label(QiniuValueConsts.BUCKET_NAME), 0, 0);
  142 + grid.add(bucket, 1, 0);
  143 + grid.add(new Label(QiniuValueConsts.BUCKET_URL), 0, 1);
  144 + grid.add(url, 1, 1);
  145 + grid.add(new Label(QiniuValueConsts.BUCKET_ZONE_NAME), 0, 2);
  146 + grid.add(zone, 1, 2);
  147 + Node okButton = dialog.getDialogPane().lookupButton(ok);
  148 + okButton.setDisable(true);
  149 + dialog.getDialogPane().setContent(grid);
  150 + Platform.runLater(bucket::requestFocus);
  151 + // 监听文本框的输入状态
  152 + bucket.textProperty().addListener((observable, oldValue, newValue) -> okButton.setDisable(newValue.trim().isEmpty() || url.getText().isEmpty()));
  153 + url.textProperty().addListener((observable, oldValue, newValue) -> okButton.setDisable(newValue.trim().isEmpty() || bucket.getText().isEmpty()));
  154 + // 结果转换器
  155 + dialog.setResultConverter(dialogButton -> {
  156 + if (dialogButton == ok) {
  157 + return new String[]{bucket.getText(), zone.getValue(), (Checker.isHyperLink(url.getText()) ?
  158 + url.getText() : "example.com")};
  159 + }
  160 + return null;
  161 + });
  162 + // 等待用户操作
  163 + Optional<String[]> result = dialog.showAndWait();
  164 + result.ifPresent(res -> {
  165 + // 处理结果
  166 + Platform.runLater(() -> MainController.getInstance().appendBucket(res[0]));
  167 + BucketBean bucketBean = new BucketBean(res[0], res[1], res[2]);
  168 + QiniuApplication.getConfigBean().getBuckets().add(bucketBean);
  169 + ConfigUtils.writeConfig();
  170 + });
  171 + }
  172 +
  173 + private Dialog<String[]> getDialog(ButtonType ok) {
  174 + Dialog<String[]> dialog = new Dialog<>();
  175 + dialog.setTitle(QiniuValueConsts.MAIN_TITLE);
  176 + dialog.setHeaderText(null);
  177 + dialog.initModality(Modality.APPLICATION_MODAL);
  178 + // 自定义确认和取消按钮
  179 + ButtonType cancel = new ButtonType(QiniuValueConsts.CANCEL, ButtonBar.ButtonData.CANCEL_CLOSE);
  180 + dialog.getDialogPane().getButtonTypes().addAll(ok, cancel);
  181 + return dialog;
  182 + }
  183 +}
  1 +package org.code4everything.qiniu.util;
  2 +
  3 +import cn.hutool.core.lang.Validator;
  4 +import cn.hutool.http.HttpUtil;
  5 +import com.zhazhapan.util.FileExecutor;
  6 +import com.zhazhapan.util.Formatter;
  7 +import com.zhazhapan.util.ThreadPool;
  8 +import com.zhazhapan.util.Utils;
  9 +import com.zhazhapan.util.dialog.Alerts;
  10 +import org.apache.log4j.Logger;
  11 +import org.code4everything.qiniu.QiniuApplication;
  12 +import org.code4everything.qiniu.constant.QiniuValueConsts;
  13 +
  14 +import java.io.File;
  15 +import java.io.IOException;
  16 +import java.io.InputStream;
  17 +import java.io.UnsupportedEncodingException;
  18 +import java.net.URL;
  19 +import java.net.URLEncoder;
  20 +
  21 +/**
  22 + * @author pantao
  23 + * @since 2018/4/14
  24 + */
  25 +public class QiniuUtils {
  26 +
  27 + private static final Logger LOGGER = Logger.getLogger(QiniuUtils.class);
  28 +
  29 + private static final String HTTP = "http";
  30 +
  31 + private QiniuUtils() {}
  32 +
  33 + /**
  34 + * 下载文件
  35 + *
  36 + * @param url 文件链接
  37 + */
  38 + public static void download(String url) {
  39 + // 验证存储路径
  40 + if (Validator.isEmpty(QiniuApplication.getConfigBean().getStoragePath())) {
  41 + // 显示存储路径输入框
  42 + String storagePath = DialogUtils.showInputDialog(null, QiniuValueConsts.CONFIG_DOWNLOAD_PATH,
  43 + Utils.getCurrentWorkDir());
  44 + if (Validator.isEmpty(storagePath)) {
  45 + return;
  46 + }
  47 + QiniuApplication.getConfigBean().setStoragePath(storagePath);
  48 + ConfigUtils.writeConfig();
  49 + }
  50 + final String dest = QiniuApplication.getConfigBean().getStoragePath();
  51 + // 下载文件
  52 + ThreadPool.executor.execute(() -> HttpUtil.downloadFile(url, dest));
  53 + }
  54 +
  55 + /**
  56 + * 检查是否连接网络
  57 + */
  58 + public static boolean checkNet() {
  59 + try {
  60 + URL url = new URL("https://www.qiniu.com/");
  61 + InputStream in = url.openStream();
  62 + in.close();
  63 + return true;
  64 + } catch (IOException e) {
  65 + LOGGER.error("there is no connection to the network");
  66 + return false;
  67 + }
  68 + }
  69 +
  70 + public static void saveLogFile(String file, String content) {
  71 + try {
  72 + FileExecutor.saveLogFile(file, content);
  73 + } catch (IOException e) {
  74 + Alerts.showError(QiniuValueConsts.MAIN_TITLE, e.getMessage());
  75 + }
  76 + }
  77 +
  78 + public static void saveFile(File file, String content) {
  79 + try {
  80 + FileExecutor.saveFile(file, content);
  81 + } catch (IOException e) {
  82 + Alerts.showError(QiniuValueConsts.MAIN_TITLE, e.getMessage());
  83 + }
  84 + }
  85 +
  86 + public static void openLink(String url) {
  87 + try {
  88 + Utils.openLink(url);
  89 + } catch (Exception e) {
  90 + Alerts.showError(QiniuValueConsts.MAIN_TITLE, e.getMessage());
  91 + }
  92 + }
  93 +
  94 + public static String getFileName(String string) {
  95 + try {
  96 + return Formatter.getFileName(string);
  97 + } catch (UnsupportedEncodingException e) {
  98 + LOGGER.error("get file name of url failed, message -> " + e.getMessage());
  99 + return "";
  100 + }
  101 + }
  102 +
  103 + /**
  104 + * 拼接链接
  105 + */
  106 + public static String buildUrl(String fileName, String domain) {
  107 + if (!domain.startsWith(HTTP)) {
  108 + domain = HTTP + "://" + domain;
  109 + }
  110 + fileName = fileName.replaceAll(" ", "qn_code_per_20").replaceAll("/", "qn_code_per_2F");
  111 + try {
  112 + fileName = URLEncoder.encode(fileName, "utf-8").replaceAll("qn_code_per_2F", "/");
  113 + return String.format("%s/%s", domain, fileName.replaceAll("qn_code_per_20", "%20"));
  114 + } catch (UnsupportedEncodingException e) {
  115 + LOGGER.error("encode url failed, message -> " + e.getMessage());
  116 + return "";
  117 + }
  118 + }
  119 +}
  1 +log4j.rootCategory=INFO, file
  2 +log4j.appender.file=org.apache.log4j.DailyRollingFileAppender
  3 +log4j.appender.file.file=logs/qiniu
  4 +log4j.appender.file.DatePattern='.'yyyy-MM-dd'.log'
  5 +log4j.appender.file.layout=org.apache.log4j.PatternLayout
  6 +log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss.SSS} %5p %c{1}:%L - %m%n
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +
  3 +<?import javafx.geometry.*?>
  4 +<?import javafx.scene.chart.*?>
  5 +<?import javafx.scene.control.*?>
  6 +<?import javafx.scene.layout.*?>
  7 +<?import javafx.scene.text.Font?>
  8 +<VBox xmlns="http://javafx.com/javafx/9" xmlns:fx="http://javafx.com/fxml/1"
  9 + fx:controller="org.code4everything.qiniu.controller.MainController">
  10 + <BorderPane maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" prefHeight="600.0"
  11 + prefWidth="700.0" VBox.vgrow="ALWAYS">
  12 + <center>
  13 + <TabPane maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" prefHeight="50.0"
  14 + prefWidth="700.0" tabClosingPolicy="UNAVAILABLE" BorderPane.alignment="CENTER">
  15 + <Tab text="文件上传">
  16 + <HBox>
  17 + <SplitPane dividerPositions="0.6" prefHeight="520.0" prefWidth="702.0" HBox.hgrow="ALWAYS">
  18 + <padding>
  19 + <Insets bottom="10.0" left="10.0" right="10.0" top="10.0"/>
  20 + </padding>
  21 + <VBox>
  22 + <TextArea fx:id="uploadStatusTA" editable="false"
  23 + maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308"
  24 + prefHeight="504.0" prefWidth="336.0" promptText="文件上传状态" VBox.vgrow="ALWAYS">
  25 + <contextMenu>
  26 + <ContextMenu>
  27 + <items>
  28 + <MenuItem mnemonicParsing="false" onAction="#clearUploadStatus"
  29 + text="清空"/>
  30 + <MenuItem mnemonicParsing="false" onAction="#saveUploadStatus"
  31 + text="另存为"/>
  32 + <MenuItem mnemonicParsing="false" onAction="#copyUploadStatus"
  33 + text="复制"/>
  34 + </items>
  35 + </ContextMenu>
  36 + </contextMenu>
  37 + <VBox.margin>
  38 + <Insets right="10.0"/>
  39 + </VBox.margin>
  40 + </TextArea>
  41 + </VBox>
  42 + <VBox prefHeight="500.0" prefWidth="171.0">
  43 + <ComboBox fx:id="prefixCB" editable="true" maxHeight="1.7976931348623157E308"
  44 + maxWidth="1.7976931348623157E308" prefHeight="35.0" prefWidth="277.0"
  45 + promptText="文件前缀, 不设置则为空" VBox.vgrow="NEVER">
  46 + <VBox.margin>
  47 + <Insets left="10.0"/>
  48 + </VBox.margin>
  49 + </ComboBox>
  50 + <HBox prefHeight="100.0" prefWidth="200.0" VBox.vgrow="NEVER">
  51 + <padding>
  52 + <Insets left="10.0" right="10.0" top="10.0"/>
  53 + </padding>
  54 + <CheckBox fx:id="recursiveCB" mnemonicParsing="false" text="文件夹递归"
  55 + selected="true"/>
  56 + <CheckBox fx:id="keepPathCB" mnemonicParsing="false" text="保持文件夹路径" selected="true">
  57 + <HBox.margin>
  58 + <Insets left="10.0"/>
  59 + </HBox.margin>
  60 + </CheckBox>
  61 + </HBox>
  62 + <TextArea fx:id="selectedFileTA" maxHeight="1.7976931348623157E308"
  63 + maxWidth="1.7976931348623157E308" onDragDropped="#dragFileDropped"
  64 + onDragOver="#dragFileOver" prefHeight="800.0" prefWidth="332.0"
  65 + promptText="文件路径, 一个文件一行, 支持拖曳文件, 支持抓取网络文件" VBox.vgrow="ALWAYS">
  66 + <VBox.margin>
  67 + <Insets left="10.0" top="10.0"/>
  68 + </VBox.margin>
  69 + </TextArea>
  70 + <HBox alignment="TOP_CENTER" VBox.vgrow="NEVER">
  71 + <VBox.margin>
  72 + <Insets left="10.0" top="10.0"/>
  73 + </VBox.margin>
  74 + <Button contentDisplay="CENTER" maxHeight="1.7976931348623157E308"
  75 + maxWidth="1.7976931348623157E308" mnemonicParsing="false"
  76 + onAction="#showOpenFileDialog" prefHeight="27.0" prefWidth="156.0"
  77 + text="选择文件"
  78 + HBox.hgrow="ALWAYS">
  79 + <HBox.margin>
  80 + <Insets right="10.0"/>
  81 + </HBox.margin>
  82 + </Button>
  83 + <Button alignment="CENTER" maxHeight="1.7976931348623157E308"
  84 + maxWidth="1.7976931348623157E308" mnemonicParsing="false"
  85 + onAction="#uploadFile" prefHeight="27.0" prefWidth="156.0" text="开始上传"
  86 + HBox.hgrow="ALWAYS">
  87 + <HBox.margin>
  88 + <Insets left="10.0"/>
  89 + </HBox.margin>
  90 + </Button>
  91 + </HBox>
  92 + </VBox>
  93 + </SplitPane>
  94 + </HBox>
  95 + </Tab>
  96 + <Tab text="资源管理">
  97 + <VBox>
  98 + <HBox alignment="CENTER_LEFT" prefHeight="52.0" prefWidth="700.0" VBox.vgrow="NEVER">
  99 + <padding>
  100 + <Insets left="10.0"/>
  101 + </padding>
  102 + <Label alignment="CENTER" text="空间域名" HBox.hgrow="NEVER">
  103 + <font>
  104 + <Font size="14.0"/>
  105 + </font>
  106 + </Label>
  107 + <TextField fx:id="domainTF" editable="false" prefHeight="27.0"
  108 + prefWidth="245.0" promptText="空间域名" HBox.hgrow="NEVER">
  109 + <HBox.margin>
  110 + <Insets left="10.0"/>
  111 + </HBox.margin>
  112 + </TextField>
  113 + <TextField fx:id="searchTF" onKeyReleased="#searchFile" prefHeight="27.0"
  114 + prefWidth="275.0" promptText="搜索文件, 支持正则表达式" HBox.hgrow="NEVER">
  115 + <HBox.margin>
  116 + <Insets left="10.0"/>
  117 + </HBox.margin>
  118 + </TextField>
  119 + <HBox alignment="CENTER_RIGHT" HBox.hgrow="ALWAYS">
  120 + <HBox.margin>
  121 + <Insets/>
  122 + </HBox.margin>
  123 + <padding>
  124 + <Insets right="15.0"/>
  125 + </padding>
  126 + <Label fx:id="lengthLabel"/>
  127 + </HBox>
  128 + </HBox>
  129 + <HBox VBox.vgrow="ALWAYS">
  130 + <TableView fx:id="resTV" editable="true" prefHeight="479.0" prefWidth="602.0"
  131 + HBox.hgrow="ALWAYS">
  132 + <columns>
  133 + <TableColumn fx:id="nameTC" prefWidth="278.0" text="文件名"/>
  134 + <TableColumn fx:id="typeTC" minWidth="5.0" prefWidth="81.0" text="文件类型"/>
  135 + <TableColumn fx:id="sizeTC" prefWidth="90.0" text="文件大小"/>
  136 + <TableColumn fx:id="timeTC" prefWidth="152.0" text="最后更新时间"/>
  137 + </columns>
  138 + <HBox.margin>
  139 + <Insets bottom="10.0" left="10.0"/>
  140 + </HBox.margin>
  141 + </TableView>
  142 + <VBox alignment="TOP_CENTER" prefHeight="200.0" prefWidth="100.0" HBox.hgrow="NEVER">
  143 + <Button mnemonicParsing="false" onAction="#refreshResourceData" text="刷新列表"
  144 + VBox.vgrow="NEVER"/>
  145 + <Button mnemonicParsing="false" onAction="#copyLink" text="复制链接" VBox.vgrow="NEVER">
  146 + <VBox.margin>
  147 + <Insets top="10.0"/>
  148 + </VBox.margin>
  149 + </Button>
  150 + <Button mnemonicParsing="false" onAction="#deleteFile" text="删除文件" VBox.vgrow="NEVER">
  151 + <VBox.margin>
  152 + <Insets top="10.0"/>
  153 + </VBox.margin>
  154 + </Button>
  155 + <Button mnemonicParsing="false" onAction="#showFileMovableDialog" text="移动到..."
  156 + VBox.vgrow="NEVER">
  157 + <VBox.margin>
  158 + <Insets top="10.0"/>
  159 + </VBox.margin>
  160 + </Button>
  161 + <Button mnemonicParsing="false" onAction="#setLife" text="生存时间" VBox.vgrow="NEVER">
  162 + <VBox.margin>
  163 + <Insets top="10.0"/>
  164 + </VBox.margin>
  165 + </Button>
  166 + <Button mnemonicParsing="false" onAction="#updateFile" text="更新镜像" VBox.vgrow="NEVER">
  167 + <VBox.margin>
  168 + <Insets top="10.0"/>
  169 + </VBox.margin>
  170 + </Button>
  171 + <Button mnemonicParsing="false" onAction="#publicDownload" text="公有下载"
  172 + VBox.vgrow="NEVER">
  173 + <VBox.margin>
  174 + <Insets top="10.0"/>
  175 + </VBox.margin>
  176 + </Button>
  177 + <Button mnemonicParsing="false" onAction="#privateDownload" text="私有下载"
  178 + VBox.vgrow="NEVER">
  179 + <VBox.margin>
  180 + <Insets top="10.0"/>
  181 + </VBox.margin>
  182 + </Button>
  183 + <Button mnemonicParsing="false" onAction="#openFile" text="打开文件" VBox.vgrow="NEVER">
  184 + <VBox.margin>
  185 + <Insets top="10.0"/>
  186 + </VBox.margin>
  187 + </Button>
  188 + <Button mnemonicParsing="false" onAction="#refreshFile" text="文件刷新" VBox.vgrow="NEVER">
  189 + <VBox.margin>
  190 + <Insets top="10.0"/>
  191 + </VBox.margin>
  192 + </Button>
  193 + <Button mnemonicParsing="false" onAction="#downloadCdnLog" text="日志下载"
  194 + VBox.vgrow="NEVER">
  195 + <VBox.margin>
  196 + <Insets top="10.0"/>
  197 + </VBox.margin>
  198 + </Button>
  199 + <VBox alignment="BOTTOM_CENTER" VBox.vgrow="ALWAYS">
  200 + <VBox.margin>
  201 + <Insets top="10.0"/>
  202 + </VBox.margin>
  203 + <padding>
  204 + <Insets bottom="10.0"/>
  205 + </padding>
  206 + <Label fx:id="sizeLabel"/>
  207 + </VBox>
  208 + </VBox>
  209 + </HBox>
  210 + </VBox>
  211 + </Tab>
  212 + <Tab text="数据统计">
  213 + <VBox>
  214 + <HBox alignment="CENTER" minHeight="-Infinity" minWidth="-Infinity" prefHeight="40.0"
  215 + prefWidth="700.0" VBox.vgrow="NEVER">
  216 + <Label text="开始日期" HBox.hgrow="NEVER">
  217 + <font>
  218 + <Font size="14.0"/>
  219 + </font>
  220 + <HBox.margin>
  221 + <Insets/>
  222 + </HBox.margin>
  223 + </Label>
  224 + <DatePicker fx:id="startDP" onAction="#dateChange" prefWidth="125.0" promptText="开始日期"
  225 + HBox.hgrow="NEVER">
  226 + <HBox.margin>
  227 + <Insets left="10.0"/>
  228 + </HBox.margin>
  229 + </DatePicker>
  230 + <Label text="结束日期">
  231 + <font>
  232 + <Font size="14.0"/>
  233 + </font>
  234 + <HBox.margin>
  235 + <Insets left="10.0"/>
  236 + </HBox.margin>
  237 + </Label>
  238 + <DatePicker fx:id="endDP" onAction="#dateChange" prefWidth="125.0" promptText="结束日期"
  239 + HBox.hgrow="NEVER">
  240 + <HBox.margin>
  241 + <Insets left="10.0"/>
  242 + </HBox.margin>
  243 + </DatePicker>
  244 + <ComboBox fx:id="fluxUnitCB" prefWidth="70.0" promptText="统计单位" HBox.hgrow="NEVER">
  245 + <HBox.margin>
  246 + <Insets left="10.0"/>
  247 + </HBox.margin>
  248 + </ComboBox>
  249 + <ComboBox fx:id="bandwidthUnitCB" prefWidth="70.0" promptText="统计单位">
  250 + <HBox.margin>
  251 + <Insets left="10.0"/>
  252 + </HBox.margin>
  253 + </ComboBox>
  254 + <Label text="时间跨度不超过31天" HBox.hgrow="NEVER">
  255 + <font>
  256 + <Font size="14.0"/>
  257 + </font>
  258 + <HBox.margin>
  259 + <Insets left="10.0"/>
  260 + </HBox.margin>
  261 + </Label>
  262 + </HBox>
  263 + <AreaChart fx:id="fluxAC" VBox.vgrow="ALWAYS">
  264 + <xAxis>
  265 + <CategoryAxis side="BOTTOM"/>
  266 + </xAxis>
  267 + <yAxis>
  268 + <NumberAxis side="LEFT"/>
  269 + </yAxis>
  270 + </AreaChart>
  271 + <AreaChart fx:id="bandwidthAC" VBox.vgrow="ALWAYS">
  272 + <xAxis>
  273 + <CategoryAxis side="BOTTOM"/>
  274 + </xAxis>
  275 + <yAxis>
  276 + <NumberAxis side="LEFT"/>
  277 + </yAxis>
  278 + </AreaChart>
  279 + </VBox>
  280 + </Tab>
  281 + </TabPane>
  282 + </center>
  283 + <top>
  284 + <VBox BorderPane.alignment="CENTER">
  285 + <BorderPane.margin>
  286 + <Insets/>
  287 + </BorderPane.margin>
  288 + <HBox VBox.vgrow="NEVER">
  289 + <padding>
  290 + <Insets top="10" bottom="10"/>
  291 + </padding>
  292 + <ComboBox fx:id="bucketCB" minHeight="-Infinity" minWidth="-Infinity" prefHeight="27.0"
  293 + prefWidth="205.0" promptText="还没有选择存储空间" HBox.hgrow="NEVER">
  294 + <HBox.margin>
  295 + <Insets left="10.0"/>
  296 + </HBox.margin>
  297 + </ComboBox>
  298 + <TextField fx:id="zoneTF" editable="false" promptText="存储区域" HBox.hgrow="NEVER">
  299 + <HBox.margin>
  300 + <Insets left="10.0"/>
  301 + </HBox.margin>
  302 + </TextField>
  303 + <Button mnemonicParsing="false" onAction="#showBucketDialog" text="添加存储空间"
  304 + HBox.hgrow="NEVER">
  305 + <HBox.margin>
  306 + <Insets left="10.0"/>
  307 + </HBox.margin>
  308 + </Button>
  309 + <Button mnemonicParsing="false" onAction="#showKeyDialog" text="重置密钥" HBox.hgrow="NEVER">
  310 + <HBox.margin>
  311 + <Insets left="10.0"/>
  312 + </HBox.margin>
  313 + </Button>
  314 + <Button mnemonicParsing="false" onAction="#openConfigFile" text="打开配置文件" HBox.hgrow="NEVER">
  315 + <HBox.margin>
  316 + <Insets left="10.0"/>
  317 + </HBox.margin>
  318 + </Button>
  319 + </HBox>
  320 + </VBox>
  321 + </top>
  322 + </BorderPane>
  323 +</VBox>
  1 +/**
  2 + *
  3 + */
  4 +package org.code4everything.qiniu;
  5 +
  6 +import org.junit.Test;
  7 +
  8 +import com.zhazhapan.util.Formatter;
  9 +
  10 +/**
  11 + * @author pantao
  12 + *
  13 + */
  14 +public class FormatterTest {
  15 +
  16 + @Test
  17 + public void testSizeToLong() {
  18 + String[] sizes = { "23.12 MB", "12.89 KB", "23 B", "23.77 GB", "89.12 TB" };
  19 + for (String size : sizes) {
  20 + System.out.println(Formatter.sizeToLong(size));
  21 + }
  22 + }
  23 +}
  1 +/**
  2 + *
  3 + */
  4 +package org.code4everything.qiniu;
  5 +
  6 +import org.junit.Test;
  7 +
  8 +import com.google.gson.JsonArray;
  9 +import com.google.gson.JsonElement;
  10 +import com.google.gson.JsonObject;
  11 +import com.google.gson.JsonParser;
  12 +import com.zhazhapan.util.Checker;
  13 +
  14 +/**
  15 + * @author pantao
  16 + *
  17 + */
  18 +public class GsonTest {
  19 +
  20 + public String configJson = "{accessKey:123456,secretKey:abcdef,buckets:[{bucket:zhazhapan,zone:华东}]}";
  21 +
  22 + @Test
  23 + public void testGson() {
  24 + JsonObject json = new JsonParser().parse(configJson).getAsJsonObject();
  25 + JsonElement buckets = json.get("buckets");
  26 + if (Checker.isNotNull(buckets)) {
  27 + JsonArray array = buckets.getAsJsonArray();
  28 + for (JsonElement element : array) {
  29 + System.out.println(((JsonObject) element).get("bucket").getAsString());
  30 + }
  31 + }
  32 + }
  33 +}
  1 +package org.code4everything.qiniu;
  2 +
  3 +import org.junit.Test;
  4 +
  5 +/**
  6 + * @author pantao
  7 + */
  8 +
  9 +public class QiniuApplicationTest {
  10 +
  11 + @Test
  12 + public void testContext() {
  13 +
  14 + }
  15 +}
  1 +/**
  2 + *
  3 + */
  4 +package org.code4everything.qiniu;
  5 +
  6 +import com.zhazhapan.util.Checker;
  7 +import org.code4everything.qiniu.util.QiniuUtils;
  8 +import org.junit.Test;
  9 +
  10 +/**
  11 + * @author pantao
  12 + */
  13 +public class RegexpTest {
  14 +
  15 + @Test
  16 + public void testGetFileName() {
  17 + String[] files = {"http://oxns0wnsc.bkt.clouddn.com/fims/css/distpicker.css", "http://oxns0wnsc.bkt.clouddn"
  18 + + ".com/fims/css/distpicker", "http://oxns0wnsc.bkt.clouddn.com/fims/css/easy-responsive-tabs.css",
  19 + "http://oxns0wnsc.bkt.clouddn.com/zhazhapan/test/%E6%BD%98%E6%BB%94-18780459330-Java%E5%BC%80%E5%8F"
  20 + + "%91.docx"};
  21 + for (String file : files) {
  22 + System.out.println(QiniuUtils.getFileName(file));
  23 + }
  24 + }
  25 +
  26 + @Test
  27 + public void testHyperlink() {
  28 + System.out.println(Checker.isHyperLink("https://portal.qiniu.com/bucket/zhazhapan/resource"));
  29 + System.out.println(Checker.isHyperLink("http://portal.qiniu.com/bucket/zhazhapan/resource"));
  30 + System.out.println(Checker.isHyperLink("portal.qiniu.com/bucket/zhazhapan/resource"));
  31 + System.out.println(Checker.isHyperLink("oxns0wnsc.bkt.clouddn.com"));
  32 + System.out.println(Checker.isHyperLink("hhttp://portal"));
  33 + System.out.println(Checker.isHyperLink("https://cn.wordpress.org/wordpress-4.8.1-zh_CN.zip"));
  34 + }
  35 +}