Finish the mirror feature. Finish the prototype of zip compress.
This commit is contained in:
parent
a4f28cca30
commit
6e0078e1d8
@ -4,3 +4,10 @@
|
|||||||
|
|
||||||
|
|
||||||
go build -mod=readonly
|
go build -mod=readonly
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### Program Arguments
|
||||||
|
|
||||||
|
-mode=mirror -username=admin -password=123456 -src=/Users/fusu/d/temp -dest=/morning
|
||||||
|
|
||||||
|
10
code/core/command.go
Normal file
10
code/core/command.go
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
package core
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从命令行输入的相关信息
|
||||||
|
*/
|
||||||
|
type Command interface {
|
||||||
|
|
||||||
|
//判断是否为命名行模式,如果是直接按照命名行模式处理,并返回true。如果不是返回false.
|
||||||
|
Cli() bool
|
||||||
|
}
|
@ -4,6 +4,13 @@ const (
|
|||||||
//用户身份的cookie字段名
|
//用户身份的cookie字段名
|
||||||
COOKIE_AUTH_KEY = "_ak"
|
COOKIE_AUTH_KEY = "_ak"
|
||||||
|
|
||||||
|
//使用用户名密码给接口授权key
|
||||||
|
USERNAME_KEY = "authUsername"
|
||||||
|
PASSWORD_KEY = "authPassword"
|
||||||
|
|
||||||
|
//默认端口号
|
||||||
|
DEFAULT_SERVER_PORT = 6010
|
||||||
|
|
||||||
//数据库表前缀 tank200表示当前应用版本是tank:2.0.x版,数据库结构发生变化必然是中型升级
|
//数据库表前缀 tank200表示当前应用版本是tank:2.0.x版,数据库结构发生变化必然是中型升级
|
||||||
TABLE_PREFIX = "tank20_"
|
TABLE_PREFIX = "tank20_"
|
||||||
|
|
||||||
@ -12,7 +19,6 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Config interface {
|
type Config interface {
|
||||||
|
|
||||||
//是否已经安装
|
//是否已经安装
|
||||||
Installed() bool
|
Installed() bool
|
||||||
//启动端口
|
//启动端口
|
||||||
|
@ -2,6 +2,9 @@ package core
|
|||||||
|
|
||||||
//该文件中记录的是应用系统中全局变量。主要有日志LOGGER和上下文CONTEXT
|
//该文件中记录的是应用系统中全局变量。主要有日志LOGGER和上下文CONTEXT
|
||||||
|
|
||||||
|
//命令行输入等相关信息
|
||||||
|
var COMMAND Command
|
||||||
|
|
||||||
//日志系统必须高保
|
//日志系统必须高保
|
||||||
//全局唯一的日志对象(在main函数中初始化)
|
//全局唯一的日志对象(在main函数中初始化)
|
||||||
var LOGGER Logger
|
var LOGGER Logger
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package core
|
package core
|
||||||
|
|
||||||
type Logger interface {
|
type Logger interface {
|
||||||
|
|
||||||
//处理日志的统一方法。
|
//处理日志的统一方法。
|
||||||
Log(prefix string, format string, v ...interface{})
|
Log(prefix string, format string, v ...interface{})
|
||||||
|
|
||||||
|
@ -72,11 +72,6 @@ func (this *AlienService) PreviewOrDownload(
|
|||||||
|
|
||||||
matter := this.matterDao.CheckByUuid(uuid)
|
matter := this.matterDao.CheckByUuid(uuid)
|
||||||
|
|
||||||
//判断是否是文件夹
|
|
||||||
if matter.Dir {
|
|
||||||
panic("不支持下载文件夹")
|
|
||||||
}
|
|
||||||
|
|
||||||
if matter.Name != filename {
|
if matter.Name != filename {
|
||||||
panic("文件信息错误")
|
panic("文件信息错误")
|
||||||
}
|
}
|
||||||
@ -117,6 +112,16 @@ func (this *AlienService) PreviewOrDownload(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//文件夹下载
|
||||||
|
if matter.Dir {
|
||||||
|
|
||||||
|
this.logger.Info("准备下载文件夹 %s", matter.Name)
|
||||||
|
|
||||||
|
//目标地点
|
||||||
|
this.matterService.AtomicDownloadDirectory(writer, request, matter)
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
//对图片处理。
|
//对图片处理。
|
||||||
needProcess, imageResizeM, imageResizeW, imageResizeH := this.imageCacheService.ResizeParams(request)
|
needProcess, imageResizeM, imageResizeW, imageResizeH := this.imageCacheService.ResizeParams(request)
|
||||||
if needProcess {
|
if needProcess {
|
||||||
@ -134,6 +139,8 @@ func (this *AlienService) PreviewOrDownload(
|
|||||||
this.matterService.DownloadFile(writer, request, matter.AbsolutePath(), matter.Name, withContentDisposition)
|
this.matterService.DownloadFile(writer, request, matter.AbsolutePath(), matter.Name, withContentDisposition)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
//文件下载次数加一,为了加快访问速度,异步进行
|
//文件下载次数加一,为了加快访问速度,异步进行
|
||||||
go util.RunWithRecovery(func() {
|
go util.RunWithRecovery(func() {
|
||||||
this.matterDao.TimesIncrement(uuid)
|
this.matterDao.TimesIncrement(uuid)
|
||||||
|
@ -59,6 +59,7 @@ func (this *BaseBean) findUser(writer http.ResponseWriter, request *http.Request
|
|||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//获取当前登录的用户,找不到就返回登录错误
|
//获取当前登录的用户,找不到就返回登录错误
|
||||||
|
@ -128,10 +128,7 @@ func (this *InstallController) getCreateSQLFromFile(tableName string) string {
|
|||||||
//1. 从当前安装目录db下去寻找建表文件。
|
//1. 从当前安装目录db下去寻找建表文件。
|
||||||
homePath := util.GetHomePath()
|
homePath := util.GetHomePath()
|
||||||
filePath := homePath + "/db/" + tableName + ".sql"
|
filePath := homePath + "/db/" + tableName + ".sql"
|
||||||
exists, err := util.PathExists(filePath)
|
exists := util.PathExists(filePath)
|
||||||
if err != nil {
|
|
||||||
panic(result.Server("从安装目录判断建表语句文件是否存在时出错!"))
|
|
||||||
}
|
|
||||||
|
|
||||||
//2. 从GOPATH下面去找,因为可能是开发环境
|
//2. 从GOPATH下面去找,因为可能是开发环境
|
||||||
if !exists {
|
if !exists {
|
||||||
@ -140,10 +137,7 @@ func (this *InstallController) getCreateSQLFromFile(tableName string) string {
|
|||||||
|
|
||||||
filePath1 := filePath
|
filePath1 := filePath
|
||||||
filePath = build.Default.GOPATH + "/src/tank/build/db/" + tableName + ".sql"
|
filePath = build.Default.GOPATH + "/src/tank/build/db/" + tableName + ".sql"
|
||||||
exists, err = util.PathExists(filePath)
|
exists = util.PathExists(filePath)
|
||||||
if err != nil {
|
|
||||||
panic(result.Server("从GOPATH判断建表语句文件是否存在时出错!"))
|
|
||||||
}
|
|
||||||
|
|
||||||
if !exists {
|
if !exists {
|
||||||
panic(result.Server("%s 或 %s 均不存在,请检查你的安装情况。", filePath1, filePath))
|
panic(result.Server("%s 或 %s 均不存在,请检查你的安装情况。", filePath1, filePath))
|
||||||
|
@ -66,6 +66,9 @@ func (this *MatterController) RegisterRoutes() map[string]func(writer http.Respo
|
|||||||
routeMap["/api/matter/detail"] = this.Wrap(this.Detail, USER_ROLE_USER)
|
routeMap["/api/matter/detail"] = this.Wrap(this.Detail, USER_ROLE_USER)
|
||||||
routeMap["/api/matter/page"] = this.Wrap(this.Page, USER_ROLE_USER)
|
routeMap["/api/matter/page"] = this.Wrap(this.Page, USER_ROLE_USER)
|
||||||
|
|
||||||
|
//本地文件映射
|
||||||
|
routeMap["/api/matter/mirror"] = this.Wrap(this.Mirror, USER_ROLE_USER)
|
||||||
|
|
||||||
return routeMap
|
return routeMap
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -443,3 +446,27 @@ func (this *MatterController) Move(writer http.ResponseWriter, request *http.Req
|
|||||||
|
|
||||||
return this.Success(nil)
|
return this.Success(nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//将本地文件映射到蓝眼云盘中去。
|
||||||
|
func (this *MatterController) Mirror(writer http.ResponseWriter, request *http.Request) *result.WebResult {
|
||||||
|
|
||||||
|
srcPath := request.FormValue("srcPath")
|
||||||
|
destPath := request.FormValue("destPath")
|
||||||
|
overwriteStr := request.FormValue("overwrite")
|
||||||
|
|
||||||
|
if srcPath == "" {
|
||||||
|
panic(result.BadRequest("srcPath必填"))
|
||||||
|
}
|
||||||
|
|
||||||
|
overwrite := false
|
||||||
|
if overwriteStr == TRUE {
|
||||||
|
overwrite = true
|
||||||
|
}
|
||||||
|
|
||||||
|
user := this.userDao.checkUser(writer, request)
|
||||||
|
|
||||||
|
this.matterService.AtomicMirror(srcPath, destPath, overwrite, user)
|
||||||
|
|
||||||
|
return this.Success(nil)
|
||||||
|
|
||||||
|
}
|
||||||
|
@ -163,6 +163,40 @@ func (this *MatterDao) CountByUserUuidAndPuuidAndDirAndName(userUuid string, puu
|
|||||||
return count
|
return count
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//统计某个用户的某个文件夹下的某个名字的文件(或文件夹)数量。
|
||||||
|
func (this *MatterDao) FindByUserUuidAndPuuidAndDirAndName(userUuid string, puuid string, dir bool, name string) *Matter {
|
||||||
|
|
||||||
|
var matter = &Matter{}
|
||||||
|
|
||||||
|
var wp = &builder.WherePair{}
|
||||||
|
|
||||||
|
if puuid != "" {
|
||||||
|
wp = wp.And(&builder.WherePair{Query: "puuid = ?", Args: []interface{}{puuid}})
|
||||||
|
}
|
||||||
|
|
||||||
|
if userUuid != "" {
|
||||||
|
wp = wp.And(&builder.WherePair{Query: "user_uuid = ?", Args: []interface{}{userUuid}})
|
||||||
|
}
|
||||||
|
|
||||||
|
if name != "" {
|
||||||
|
wp = wp.And(&builder.WherePair{Query: "name = ?", Args: []interface{}{name}})
|
||||||
|
}
|
||||||
|
|
||||||
|
wp = wp.And(&builder.WherePair{Query: "dir = ?", Args: []interface{}{dir}})
|
||||||
|
|
||||||
|
db := core.CONTEXT.GetDB().Where(wp.Query, wp.Args...).First(matter)
|
||||||
|
|
||||||
|
if db.Error != nil {
|
||||||
|
if db.Error.Error() == result.DB_ERROR_NOT_FOUND {
|
||||||
|
return nil
|
||||||
|
} else {
|
||||||
|
this.PanicError(db.Error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return matter
|
||||||
|
}
|
||||||
|
|
||||||
//获取某个用户的某个文件夹下的某个名字的文件(或文件夹)列表
|
//获取某个用户的某个文件夹下的某个名字的文件(或文件夹)列表
|
||||||
func (this *MatterDao) ListByUserUuidAndPuuidAndDirAndName(userUuid string, puuid string, dir bool, name string) []*Matter {
|
func (this *MatterDao) ListByUserUuidAndPuuidAndDirAndName(userUuid string, puuid string, dir bool, name string) []*Matter {
|
||||||
|
|
||||||
@ -318,6 +352,8 @@ func (this *MatterDao) CountBetweenTime(startTime time.Time, endTime time.Time)
|
|||||||
|
|
||||||
//获取一段时间中文件总大小
|
//获取一段时间中文件总大小
|
||||||
func (this *MatterDao) SizeBetweenTime(startTime time.Time, endTime time.Time) int64 {
|
func (this *MatterDao) SizeBetweenTime(startTime time.Time, endTime time.Time) int64 {
|
||||||
|
|
||||||
|
//TODO: 所有函数汇总的SQL均需要先count询问,再处理。
|
||||||
var size int64
|
var size int64
|
||||||
db := core.CONTEXT.GetDB().Model(&Matter{}).Where("create_time >= ? AND create_time <= ?", startTime, endTime).Select("SUM(size)")
|
db := core.CONTEXT.GetDB().Model(&Matter{}).Where("create_time >= ? AND create_time <= ?", startTime, endTime).Select("SUM(size)")
|
||||||
this.PanicError(db.Error)
|
this.PanicError(db.Error)
|
||||||
@ -360,6 +396,28 @@ func (this *MatterDao) checkByUserUuidAndPath(userUuid string, path string) *Mat
|
|||||||
return matter
|
return matter
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//获取一个文件夹中文件总大小
|
||||||
|
func (this *MatterDao) SumSizeByUserUuidAndPath(userUuid string, path string) int64 {
|
||||||
|
|
||||||
|
var wp = &builder.WherePair{Query: "user_uuid = ? AND path like ?", Args: []interface{}{userUuid, path + "%"}}
|
||||||
|
|
||||||
|
var count int64
|
||||||
|
db := core.CONTEXT.GetDB().Model(&Matter{}).Where(wp.Query, wp.Args...).Count(&count)
|
||||||
|
if count == 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
var sumSize int64
|
||||||
|
db = core.CONTEXT.GetDB().Model(&Matter{}).Where(wp.Query, wp.Args...).Select("SUM(size)")
|
||||||
|
this.PanicError(db.Error)
|
||||||
|
row := db.Row()
|
||||||
|
err := row.Scan(&sumSize)
|
||||||
|
util.PanicError(err)
|
||||||
|
|
||||||
|
return sumSize
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
//执行清理操作
|
//执行清理操作
|
||||||
func (this *MatterDao) Cleanup() {
|
func (this *MatterDao) Cleanup() {
|
||||||
this.logger.Info("[MatterDao]执行清理:清除数据库中所有Matter记录。删除磁盘中所有Matter文件。")
|
this.logger.Info("[MatterDao]执行清理:清除数据库中所有Matter记录。删除磁盘中所有Matter文件。")
|
||||||
|
@ -3,7 +3,6 @@ package rest
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/eyebluecn/tank/code/core"
|
"github.com/eyebluecn/tank/code/core"
|
||||||
|
|
||||||
"github.com/eyebluecn/tank/code/tool/util"
|
"github.com/eyebluecn/tank/code/tool/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -12,6 +11,8 @@ const (
|
|||||||
MATTER_ROOT = "root"
|
MATTER_ROOT = "root"
|
||||||
//cache文件夹名称
|
//cache文件夹名称
|
||||||
MATTER_CACHE = "cache"
|
MATTER_CACHE = "cache"
|
||||||
|
//压缩文件的临时目录
|
||||||
|
MATTER_ZIP = "zip"
|
||||||
//matter名称最大长度
|
//matter名称最大长度
|
||||||
MATTER_NAME_MAX_LENGTH = 200
|
MATTER_NAME_MAX_LENGTH = 200
|
||||||
//matter文件夹最大深度
|
//matter文件夹最大深度
|
||||||
@ -80,3 +81,11 @@ func GetUserCacheRootDir(username string) (rootDirPath string) {
|
|||||||
|
|
||||||
return rootDirPath
|
return rootDirPath
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//获取到用户压缩临时文件的根目录。
|
||||||
|
func GetUserZipRootDir(username string) (rootDirPath string) {
|
||||||
|
|
||||||
|
rootDirPath = fmt.Sprintf("%s/%s/%s", core.CONFIG.MatterPath(), username, MATTER_ZIP)
|
||||||
|
|
||||||
|
return rootDirPath
|
||||||
|
}
|
||||||
|
@ -1,15 +1,21 @@
|
|||||||
package rest
|
package rest
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"archive/zip"
|
||||||
|
"fmt"
|
||||||
"github.com/eyebluecn/tank/code/core"
|
"github.com/eyebluecn/tank/code/core"
|
||||||
|
"github.com/eyebluecn/tank/code/tool/builder"
|
||||||
"github.com/eyebluecn/tank/code/tool/download"
|
"github.com/eyebluecn/tank/code/tool/download"
|
||||||
"github.com/eyebluecn/tank/code/tool/result"
|
"github.com/eyebluecn/tank/code/tool/result"
|
||||||
"github.com/eyebluecn/tank/code/tool/util"
|
"github.com/eyebluecn/tank/code/tool/util"
|
||||||
"io"
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -69,6 +75,132 @@ func (this *MatterService) DownloadFile(
|
|||||||
download.DownloadFile(writer, request, filePath, filename, withContentDisposition)
|
download.DownloadFile(writer, request, filePath, filename, withContentDisposition)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//下载文件夹
|
||||||
|
func (this *MatterService) AtomicDownloadDirectory(
|
||||||
|
writer http.ResponseWriter,
|
||||||
|
request *http.Request,
|
||||||
|
matter *Matter) {
|
||||||
|
|
||||||
|
if matter == nil {
|
||||||
|
panic(result.BadRequest("matter不能为nil"))
|
||||||
|
}
|
||||||
|
|
||||||
|
if !matter.Dir {
|
||||||
|
panic(result.BadRequest("matter 只能是文件夹"))
|
||||||
|
}
|
||||||
|
|
||||||
|
//操作锁
|
||||||
|
this.userService.MatterLock(matter.UserUuid)
|
||||||
|
defer this.userService.MatterUnlock(matter.UserUuid)
|
||||||
|
|
||||||
|
//验证文件夹中文件总大小。
|
||||||
|
sumSize := this.matterDao.SumSizeByUserUuidAndPath(matter.UserUuid, matter.Path)
|
||||||
|
this.logger.Info("文件夹 %s 大小为 %s", matter.Name, util.HumanFileSize(sumSize))
|
||||||
|
|
||||||
|
//TODO: 文件夹下载的大小限制
|
||||||
|
//准备zip放置的目录。
|
||||||
|
destZipDirPath := fmt.Sprintf("%s/%d", GetUserZipRootDir(matter.Username), time.Now().UnixNano()/1e6)
|
||||||
|
util.MakeDirAll(destZipDirPath)
|
||||||
|
|
||||||
|
destZipName := fmt.Sprintf("%s.zip", matter.Name)
|
||||||
|
|
||||||
|
destZipPath := fmt.Sprintf("%s/%s", destZipDirPath, destZipName)
|
||||||
|
|
||||||
|
//destZipFile, err := os.Create(destZipPath)
|
||||||
|
//util.PanicError(err)
|
||||||
|
//
|
||||||
|
//defer func() {
|
||||||
|
// err := destZipFile.Close()
|
||||||
|
// util.PanicError(err)
|
||||||
|
//}()
|
||||||
|
//
|
||||||
|
//zipWriter := zip.NewWriter(destZipFile)
|
||||||
|
//defer func() {
|
||||||
|
// err := zipWriter.Close()
|
||||||
|
// util.PanicError(err)
|
||||||
|
//}()
|
||||||
|
|
||||||
|
//this.zipCompress(matter, GetUserFileRootDir(matter.Username), zipWriter)
|
||||||
|
|
||||||
|
util.Zip(matter.AbsolutePath(), destZipPath)
|
||||||
|
|
||||||
|
//下载
|
||||||
|
download.DownloadFile(writer, request, destZipPath, destZipName, true)
|
||||||
|
|
||||||
|
//TODO: 删除临时压缩文件
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//zip压缩一个matter.
|
||||||
|
func (this *MatterService) zipCompress(matter *Matter, prefix string, zipWriter *zip.Writer) {
|
||||||
|
|
||||||
|
if matter == nil {
|
||||||
|
panic(result.BadRequest("matter不能为nil"))
|
||||||
|
}
|
||||||
|
|
||||||
|
fileInfo, err := os.Stat(matter.AbsolutePath())
|
||||||
|
this.PanicError(err)
|
||||||
|
|
||||||
|
fmt.Println("遍历文件: " + matter.AbsolutePath())
|
||||||
|
|
||||||
|
if matter.Dir {
|
||||||
|
|
||||||
|
//获取下一级的文件。
|
||||||
|
prefix = prefix + "/" + matter.Name
|
||||||
|
|
||||||
|
sortArray := []builder.OrderPair{
|
||||||
|
{
|
||||||
|
Key: "path",
|
||||||
|
Value: "ASC",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
matters := this.matterDao.List(matter.Uuid, matter.UserUuid, sortArray)
|
||||||
|
|
||||||
|
// 通过文件信息,创建 zip 的文件信息
|
||||||
|
fileHeader, err := zip.FileInfoHeader(fileInfo)
|
||||||
|
this.PanicError(err)
|
||||||
|
|
||||||
|
//写入头信息。目录无需写入内容。目录的头部要去掉斜杠,尾部要加上斜杠。
|
||||||
|
fileHeader.Name = strings.TrimPrefix(prefix+"/", string(filepath.Separator))
|
||||||
|
|
||||||
|
fmt.Println("文件夹头: " + fileHeader.Name)
|
||||||
|
|
||||||
|
// 写入文件信息,并返回一个 Write 结构
|
||||||
|
_, err = zipWriter.CreateHeader(fileHeader)
|
||||||
|
this.PanicError(err)
|
||||||
|
|
||||||
|
for _, subMatter := range matters {
|
||||||
|
this.zipCompress(subMatter, prefix, zipWriter)
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
fileHeader, err := zip.FileInfoHeader(fileInfo)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
//第一个斜杠不需要。
|
||||||
|
fileHeader.Name = strings.TrimPrefix(prefix+"/"+matter.Name, string(filepath.Separator))
|
||||||
|
|
||||||
|
fmt.Println("文件头部: " + fileHeader.Name)
|
||||||
|
|
||||||
|
writer, err := zipWriter.CreateHeader(fileHeader)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
file, err := os.Open(matter.AbsolutePath())
|
||||||
|
_, err = io.Copy(writer, file)
|
||||||
|
defer func() {
|
||||||
|
err := file.Close()
|
||||||
|
this.PanicError(err)
|
||||||
|
}()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
//删除文件
|
//删除文件
|
||||||
func (this *MatterService) AtomicDelete(matter *Matter) {
|
func (this *MatterService) AtomicDelete(matter *Matter) {
|
||||||
|
|
||||||
@ -117,8 +249,7 @@ func (this *MatterService) Upload(file io.Reader, user *User, dirMatter *Matter,
|
|||||||
util.MakeDirAll(dirAbsolutePath)
|
util.MakeDirAll(dirAbsolutePath)
|
||||||
|
|
||||||
//如果文件已经存在了,那么直接覆盖。
|
//如果文件已经存在了,那么直接覆盖。
|
||||||
exist, err := util.PathExists(fileAbsolutePath)
|
exist := util.PathExists(fileAbsolutePath)
|
||||||
this.PanicError(err)
|
|
||||||
if exist {
|
if exist {
|
||||||
this.logger.Error("%s已经存在,将其删除", fileAbsolutePath)
|
this.logger.Error("%s已经存在,将其删除", fileAbsolutePath)
|
||||||
removeError := os.Remove(fileAbsolutePath)
|
removeError := os.Remove(fileAbsolutePath)
|
||||||
@ -214,12 +345,10 @@ func (this *MatterService) createDirectory(dirMatter *Matter, name string, user
|
|||||||
panic(result.BadRequest(`名称中不能包含以下特殊符号:< > | * ? / \`))
|
panic(result.BadRequest(`名称中不能包含以下特殊符号:< > | * ? / \`))
|
||||||
}
|
}
|
||||||
|
|
||||||
//判断同级文件夹中是否有同名的文件夹
|
//判断同级文件夹中是否有同名的文件夹。存在了直接返回即可。
|
||||||
count := this.matterDao.CountByUserUuidAndPuuidAndDirAndName(user.Uuid, dirMatter.Uuid, true, name)
|
matter := this.matterDao.FindByUserUuidAndPuuidAndDirAndName(user.Uuid, dirMatter.Uuid, true, name)
|
||||||
|
if matter != nil {
|
||||||
if count > 0 {
|
return matter
|
||||||
|
|
||||||
panic(result.BadRequest("%s 已经存在了,请使用其他名称。", name))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
parts := strings.Split(dirMatter.Path, "/")
|
parts := strings.Split(dirMatter.Path, "/")
|
||||||
@ -240,7 +369,7 @@ func (this *MatterService) createDirectory(dirMatter *Matter, name string, user
|
|||||||
this.logger.Info("Create Directory: %s", dirPath)
|
this.logger.Info("Create Directory: %s", dirPath)
|
||||||
|
|
||||||
//数据库中创建文件夹。
|
//数据库中创建文件夹。
|
||||||
matter := &Matter{
|
matter = &Matter{
|
||||||
Puuid: dirMatter.Uuid,
|
Puuid: dirMatter.Uuid,
|
||||||
UserUuid: user.Uuid,
|
UserUuid: user.Uuid,
|
||||||
Username: user.Username,
|
Username: user.Username,
|
||||||
@ -569,7 +698,96 @@ func (this *MatterService) AtomicRename(matter *Matter, name string, user *User)
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
//根据一个文件夹路径,依次创建,找到最后一个文件夹的matter,如果中途出错,返回err.
|
//将本地文件映射到蓝眼云盘中去。
|
||||||
|
func (this *MatterService) AtomicMirror(srcPath string, destPath string, overwrite bool, user *User) {
|
||||||
|
|
||||||
|
if user == nil {
|
||||||
|
panic(result.BadRequest("user cannot be nil"))
|
||||||
|
}
|
||||||
|
|
||||||
|
//操作锁
|
||||||
|
this.userService.MatterLock(user.Uuid)
|
||||||
|
defer this.userService.MatterUnlock(user.Uuid)
|
||||||
|
|
||||||
|
//验证参数。
|
||||||
|
if destPath == "" {
|
||||||
|
panic(result.BadRequest("dest 参数必填"))
|
||||||
|
}
|
||||||
|
|
||||||
|
destDirMatter := this.CreateDirectories(user, destPath)
|
||||||
|
|
||||||
|
this.mirror(srcPath, destDirMatter, overwrite, user)
|
||||||
|
}
|
||||||
|
|
||||||
|
//将本地文件/文件夹映射到蓝眼云盘中去。
|
||||||
|
func (this *MatterService) mirror(srcPath string, destDirMatter *Matter, overwrite bool, user *User) {
|
||||||
|
|
||||||
|
if user == nil {
|
||||||
|
panic(result.BadRequest("user cannot be nil"))
|
||||||
|
}
|
||||||
|
|
||||||
|
fileStat, err := os.Stat(srcPath)
|
||||||
|
if err != nil {
|
||||||
|
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
panic(result.BadRequest("srcPath %s not exist", srcPath))
|
||||||
|
} else {
|
||||||
|
|
||||||
|
panic(result.BadRequest("srcPath err %s %s", srcPath, err.Error()))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
this.logger.Info("mirror srcPath = %s destPath = %s", srcPath, destDirMatter.Path)
|
||||||
|
|
||||||
|
if fileStat.IsDir() {
|
||||||
|
|
||||||
|
//判断当前文件夹下,文件是否已经存在了。
|
||||||
|
srcDirMatter := this.matterDao.FindByUserUuidAndPuuidAndDirAndName(user.Uuid, destDirMatter.Uuid, true, fileStat.Name())
|
||||||
|
|
||||||
|
if srcDirMatter == nil {
|
||||||
|
srcDirMatter = this.createDirectory(destDirMatter, fileStat.Name(), user)
|
||||||
|
}
|
||||||
|
|
||||||
|
fileInfos, err := ioutil.ReadDir(srcPath)
|
||||||
|
this.PanicError(err)
|
||||||
|
|
||||||
|
//递归处理本文件夹下的文件或文件夹
|
||||||
|
for _, fileInfo := range fileInfos {
|
||||||
|
|
||||||
|
path := fmt.Sprintf("%s/%s", srcPath, fileInfo.Name())
|
||||||
|
this.mirror(path, srcDirMatter, overwrite, user)
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
//判断当前文件夹下,文件是否已经存在了。
|
||||||
|
matter := this.matterDao.FindByUserUuidAndPuuidAndDirAndName(user.Uuid, destDirMatter.Uuid, false, fileStat.Name())
|
||||||
|
if matter != nil {
|
||||||
|
//如果是覆盖,那么删除之前的文件
|
||||||
|
if overwrite {
|
||||||
|
this.matterDao.Delete(matter)
|
||||||
|
} else {
|
||||||
|
//直接完成。
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//准备直接从本地上传了。
|
||||||
|
file, err := os.Open(srcPath)
|
||||||
|
this.PanicError(err)
|
||||||
|
defer func() {
|
||||||
|
err := file.Close()
|
||||||
|
this.PanicError(err)
|
||||||
|
}()
|
||||||
|
|
||||||
|
this.Upload(file, user, destDirMatter, fileStat.Name(), true)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//根据一个文件夹路径,依次创建,找到最后一个文件夹的matter,如果中途出错,返回err. 如果存在了那就直接返回即可。
|
||||||
func (this *MatterService) CreateDirectories(user *User, dirPath string) *Matter {
|
func (this *MatterService) CreateDirectories(user *User, dirPath string) *Matter {
|
||||||
|
|
||||||
if dirPath == "" {
|
if dirPath == "" {
|
||||||
|
@ -36,6 +36,7 @@ func (this *PreferenceController) RegisterRoutes() map[string]func(writer http.R
|
|||||||
routeMap := make(map[string]func(writer http.ResponseWriter, request *http.Request))
|
routeMap := make(map[string]func(writer http.ResponseWriter, request *http.Request))
|
||||||
|
|
||||||
//每个Controller需要主动注册自己的路由。
|
//每个Controller需要主动注册自己的路由。
|
||||||
|
routeMap["/api/preference/ping"] = this.Wrap(this.Ping, USER_ROLE_GUEST)
|
||||||
routeMap["/api/preference/fetch"] = this.Wrap(this.Fetch, USER_ROLE_GUEST)
|
routeMap["/api/preference/fetch"] = this.Wrap(this.Fetch, USER_ROLE_GUEST)
|
||||||
routeMap["/api/preference/edit"] = this.Wrap(this.Edit, USER_ROLE_ADMINISTRATOR)
|
routeMap["/api/preference/edit"] = this.Wrap(this.Edit, USER_ROLE_ADMINISTRATOR)
|
||||||
routeMap["/api/preference/system_cleanup"] = this.Wrap(this.SystemCleanup, USER_ROLE_ADMINISTRATOR)
|
routeMap["/api/preference/system_cleanup"] = this.Wrap(this.SystemCleanup, USER_ROLE_ADMINISTRATOR)
|
||||||
@ -43,6 +44,13 @@ func (this *PreferenceController) RegisterRoutes() map[string]func(writer http.R
|
|||||||
return routeMap
|
return routeMap
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//简单验证蓝眼云盘服务是否已经启动了。
|
||||||
|
func (this *PreferenceController) Ping(writer http.ResponseWriter, request *http.Request) *result.WebResult {
|
||||||
|
|
||||||
|
return this.Success(nil)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
//查看某个偏好设置的详情。
|
//查看某个偏好设置的详情。
|
||||||
func (this *PreferenceController) Fetch(writer http.ResponseWriter, request *http.Request) *result.WebResult {
|
func (this *PreferenceController) Fetch(writer http.ResponseWriter, request *http.Request) *result.WebResult {
|
||||||
|
|
||||||
|
@ -42,28 +42,30 @@ func (this *UserController) RegisterRoutes() map[string]func(writer http.Respons
|
|||||||
|
|
||||||
//使用用户名和密码进行登录。
|
//使用用户名和密码进行登录。
|
||||||
//参数:
|
//参数:
|
||||||
// @email:邮箱
|
// @username:用户名(也可以输入邮箱)
|
||||||
// @password:密码
|
// @password:密码
|
||||||
func (this *UserController) Login(writer http.ResponseWriter, request *http.Request) *result.WebResult {
|
func (this *UserController) Login(writer http.ResponseWriter, request *http.Request) *result.WebResult {
|
||||||
|
|
||||||
email := request.FormValue("email")
|
username := request.FormValue("username")
|
||||||
password := request.FormValue("password")
|
password := request.FormValue("password")
|
||||||
|
|
||||||
if "" == email || "" == password {
|
if "" == username || "" == password {
|
||||||
|
|
||||||
panic(result.BadRequest("请输入邮箱和密码"))
|
panic(result.BadRequest("请输入用户名和密码"))
|
||||||
}
|
}
|
||||||
|
|
||||||
user := this.userDao.FindByEmail(email)
|
user := this.userDao.FindByUsername(username)
|
||||||
if user == nil {
|
if user == nil {
|
||||||
|
user = this.userDao.FindByEmail(username)
|
||||||
|
if user == nil {
|
||||||
|
panic(result.BadRequest("用户名或密码错误"))
|
||||||
|
}
|
||||||
|
|
||||||
panic(result.BadRequest("邮箱或密码错误"))
|
}
|
||||||
|
|
||||||
} else {
|
|
||||||
if !util.MatchBcrypt(password, user.Password) {
|
if !util.MatchBcrypt(password, user.Password) {
|
||||||
|
|
||||||
panic(result.BadRequest("邮箱或密码错误"))
|
panic(result.BadRequest("用户名或密码错误"))
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//登录成功,设置Cookie。有效期30天。
|
//登录成功,设置Cookie。有效期30天。
|
||||||
|
@ -4,6 +4,8 @@ import (
|
|||||||
"github.com/eyebluecn/tank/code/core"
|
"github.com/eyebluecn/tank/code/core"
|
||||||
"github.com/eyebluecn/tank/code/tool/cache"
|
"github.com/eyebluecn/tank/code/tool/cache"
|
||||||
"github.com/eyebluecn/tank/code/tool/result"
|
"github.com/eyebluecn/tank/code/tool/result"
|
||||||
|
"github.com/eyebluecn/tank/code/tool/util"
|
||||||
|
uuid "github.com/nu7hatch/gouuid"
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@ -76,12 +78,9 @@ func (this *UserService) PreHandle(writer http.ResponseWriter, request *http.Req
|
|||||||
//登录身份有效期以数据库中记录的为准
|
//登录身份有效期以数据库中记录的为准
|
||||||
|
|
||||||
//验证用户是否已经登录。
|
//验证用户是否已经登录。
|
||||||
sessionCookie, err := request.Cookie(core.COOKIE_AUTH_KEY)
|
sessionId := util.GetSessionUuidFromRequest(request, core.COOKIE_AUTH_KEY)
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
sessionId := sessionCookie.Value
|
if sessionId != "" {
|
||||||
|
|
||||||
//去缓存中捞取
|
//去缓存中捞取
|
||||||
cacheItem, err := core.CONTEXT.GetSessionCache().Value(sessionId)
|
cacheItem, err := core.CONTEXT.GetSessionCache().Value(sessionId)
|
||||||
@ -91,7 +90,7 @@ func (this *UserService) PreHandle(writer http.ResponseWriter, request *http.Req
|
|||||||
|
|
||||||
//缓存中没有,尝试去数据库捞取
|
//缓存中没有,尝试去数据库捞取
|
||||||
if cacheItem == nil || cacheItem.Data() == nil {
|
if cacheItem == nil || cacheItem.Data() == nil {
|
||||||
session := this.sessionDao.FindByUuid(sessionCookie.Value)
|
session := this.sessionDao.FindByUuid(sessionId)
|
||||||
if session != nil {
|
if session != nil {
|
||||||
duration := session.ExpireTime.Sub(time.Now())
|
duration := session.ExpireTime.Sub(time.Now())
|
||||||
if duration <= 0 {
|
if duration <= 0 {
|
||||||
@ -100,12 +99,47 @@ func (this *UserService) PreHandle(writer http.ResponseWriter, request *http.Req
|
|||||||
user := this.userDao.FindByUuid(session.UserUuid)
|
user := this.userDao.FindByUuid(session.UserUuid)
|
||||||
if user != nil {
|
if user != nil {
|
||||||
//将用户装填进缓存中
|
//将用户装填进缓存中
|
||||||
core.CONTEXT.GetSessionCache().Add(sessionCookie.Value, duration, user)
|
core.CONTEXT.GetSessionCache().Add(sessionId, duration, user)
|
||||||
} else {
|
} else {
|
||||||
this.logger.Error("没有找到对应的user " + session.UserUuid)
|
this.logger.Error("没有找到对应的user %s", session.UserUuid)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//再尝试读取一次,这次从 USERNAME_KEY PASSWORD_KEY 中装填用户登录信息
|
||||||
|
cacheItem, err := core.CONTEXT.GetSessionCache().Value(sessionId)
|
||||||
|
if err != nil {
|
||||||
|
this.logger.Error("获取缓存时出错了" + err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
if cacheItem == nil || cacheItem.Data() == nil {
|
||||||
|
username := request.FormValue(core.USERNAME_KEY)
|
||||||
|
password := request.FormValue(core.PASSWORD_KEY)
|
||||||
|
|
||||||
|
if username != "" && password != "" {
|
||||||
|
|
||||||
|
user := this.userDao.FindByUsername(username)
|
||||||
|
if user == nil {
|
||||||
|
this.logger.Error("%s 用户名或密码错误", core.USERNAME_KEY)
|
||||||
|
} else {
|
||||||
|
|
||||||
|
if !util.MatchBcrypt(password, user.Password) {
|
||||||
|
this.logger.Error("%s 用户名或密码错误", core.USERNAME_KEY)
|
||||||
|
} else {
|
||||||
|
//装填一个临时的session用作后续使用。
|
||||||
|
this.logger.Info("准备装载一个临时的用作。")
|
||||||
|
timeUUID, _ := uuid.NewV4()
|
||||||
|
uuidStr := string(timeUUID.String())
|
||||||
|
request.Form[core.COOKIE_AUTH_KEY] = []string{uuidStr}
|
||||||
|
|
||||||
|
//将用户装填进缓存中
|
||||||
|
core.CONTEXT.GetSessionCache().Add(uuidStr, 10*time.Second, user)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
151
code/support/tank_command.go
Normal file
151
code/support/tank_command.go
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
package support
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"github.com/eyebluecn/tank/code/core"
|
||||||
|
"github.com/eyebluecn/tank/code/tool/result"
|
||||||
|
"github.com/eyebluecn/tank/code/tool/util"
|
||||||
|
jsoniter "github.com/json-iterator/go"
|
||||||
|
"golang.org/x/crypto/ssh/terminal"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
//启动web服务,默认是这种方式
|
||||||
|
MODE_WEB = "web"
|
||||||
|
//映射本地文件到云盘中
|
||||||
|
MODE_MIRROR = "mirror"
|
||||||
|
)
|
||||||
|
|
||||||
|
//命令行输入相关的对象
|
||||||
|
type TankCommand struct {
|
||||||
|
//模式
|
||||||
|
mode string
|
||||||
|
|
||||||
|
//蓝眼云盘的主机,需要带上协议和端口号。默认: http://127.0.0.1:core.DEFAULT_SERVER_PORT
|
||||||
|
host string
|
||||||
|
//用户名
|
||||||
|
username string
|
||||||
|
//密码
|
||||||
|
password string
|
||||||
|
|
||||||
|
//源文件/文件夹,本地绝对路径
|
||||||
|
src string
|
||||||
|
//目标(表示的是文件夹)路径,蓝眼云盘中的路径。相对于root的路径。
|
||||||
|
dest string
|
||||||
|
//同名文件或文件夹是否直接替换 true 全部替换; false 跳过
|
||||||
|
overwrite bool
|
||||||
|
}
|
||||||
|
|
||||||
|
//第三级. 从程序参数中读取配置项
|
||||||
|
func (this *TankCommand) Cli() bool {
|
||||||
|
|
||||||
|
//超级管理员信息
|
||||||
|
modePtr := flag.String("mode", this.mode, "cli mode web/mirror")
|
||||||
|
hostPtr := flag.String("host", this.username, "tank host")
|
||||||
|
usernamePtr := flag.String("username", this.username, "username")
|
||||||
|
passwordPtr := flag.String("password", this.password, "password")
|
||||||
|
srcPtr := flag.String("src", this.src, "src absolute path")
|
||||||
|
destPtr := flag.String("dest", this.dest, "destination path in tank.")
|
||||||
|
overwritePtr := flag.Bool("overwrite", this.overwrite, "whether same file overwrite")
|
||||||
|
|
||||||
|
//flag.Parse()方法必须要在使用之前调用。
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
this.mode = *modePtr
|
||||||
|
this.host = *hostPtr
|
||||||
|
this.username = *usernamePtr
|
||||||
|
this.password = *passwordPtr
|
||||||
|
this.src = *srcPtr
|
||||||
|
this.dest = *destPtr
|
||||||
|
this.overwrite = *overwritePtr
|
||||||
|
|
||||||
|
//准备模式
|
||||||
|
if this.mode == "" || strings.ToLower(this.mode) == MODE_WEB {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
//准备蓝眼云盘地址
|
||||||
|
if this.host == "" {
|
||||||
|
this.host = fmt.Sprintf("http://127.0.0.1:%d", core.DEFAULT_SERVER_PORT)
|
||||||
|
}
|
||||||
|
|
||||||
|
//准备用户名
|
||||||
|
if this.username == "" {
|
||||||
|
fmt.Println("用户名必填")
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
//准备密码
|
||||||
|
if this.password == "" {
|
||||||
|
|
||||||
|
if util.EnvDevelopment() {
|
||||||
|
|
||||||
|
fmt.Println("IDE中请运行请直接使用 -password yourPassword 的形式输入密码")
|
||||||
|
return true
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
fmt.Print("Enter Password:")
|
||||||
|
bytePassword, err := terminal.ReadPassword(int(syscall.Stdin))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
this.password = string(bytePassword)
|
||||||
|
fmt.Println()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.ToLower(this.mode) == MODE_MIRROR {
|
||||||
|
|
||||||
|
this.HandleMirror()
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
fmt.Printf("不能处理命名行模式: %s \r\n", this.mode)
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
//处理本地映射的情形
|
||||||
|
func (this *TankCommand) HandleMirror() {
|
||||||
|
|
||||||
|
fmt.Printf("开始映射本地文件 %s 到蓝眼云盘 %s\r\n", this.src, this.dest)
|
||||||
|
|
||||||
|
urlString := fmt.Sprintf("%s/api/matter/mirror", this.host)
|
||||||
|
|
||||||
|
params := url.Values{
|
||||||
|
"srcPath": {this.src},
|
||||||
|
"destPath": {this.dest},
|
||||||
|
"overwrite": {fmt.Sprintf("%v", this.overwrite)},
|
||||||
|
core.USERNAME_KEY: {this.username},
|
||||||
|
core.PASSWORD_KEY: {this.password},
|
||||||
|
}
|
||||||
|
|
||||||
|
response, err := http.PostForm(urlString, params)
|
||||||
|
util.PanicError(err)
|
||||||
|
|
||||||
|
bodyBytes, err := ioutil.ReadAll(response.Body)
|
||||||
|
|
||||||
|
webResult := &result.WebResult{}
|
||||||
|
|
||||||
|
err = jsoniter.ConfigCompatibleWithStandardLibrary.Unmarshal(bodyBytes, webResult)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("返回格式错误!%s \r\n", err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if webResult.Code == result.CODE_WRAPPER_OK.Code {
|
||||||
|
fmt.Println("success")
|
||||||
|
} else {
|
||||||
|
fmt.Printf("error %s\r\n", webResult.Msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -107,8 +107,8 @@ func (this *TankConfig) Init() {
|
|||||||
stream.WriteString(t.Local().Format("2006-01-02 15:04:05"))
|
stream.WriteString(t.Local().Format("2006-01-02 15:04:05"))
|
||||||
}, nil)
|
}, nil)
|
||||||
|
|
||||||
//默认从6010端口启动
|
//默认从core.DEFAULT_SERVER_PORT端口启动
|
||||||
this.serverPort = 6010
|
this.serverPort = core.DEFAULT_SERVER_PORT
|
||||||
|
|
||||||
this.ReadFromConfigFile()
|
this.ReadFromConfigFile()
|
||||||
|
|
||||||
|
@ -116,8 +116,7 @@ func (this *TankLogger) maintain() {
|
|||||||
this.Log("删除日志文件 %s", oldDestPath)
|
this.Log("删除日志文件 %s", oldDestPath)
|
||||||
|
|
||||||
//删除文件
|
//删除文件
|
||||||
exists, err := util.PathExists(oldDestPath)
|
exists := util.PathExists(oldDestPath)
|
||||||
util.PanicError(err)
|
|
||||||
if exists {
|
if exists {
|
||||||
err = os.Remove(oldDestPath)
|
err = os.Remove(oldDestPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -2,7 +2,6 @@ package support
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/eyebluecn/tank/code/core"
|
"github.com/eyebluecn/tank/code/core"
|
||||||
"github.com/eyebluecn/tank/code/rest"
|
"github.com/eyebluecn/tank/code/rest"
|
||||||
"github.com/eyebluecn/tank/code/tool/result"
|
"github.com/eyebluecn/tank/code/tool/result"
|
||||||
@ -171,6 +170,7 @@ func (this *TankRouter) ServeHTTP(writer http.ResponseWriter, request *http.Requ
|
|||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
//当作静态资源处理。默认从当前文件下面的static文件夹中取东西。
|
//当作静态资源处理。默认从当前文件下面的static文件夹中取东西。
|
||||||
dir := util.GetHtmlPath()
|
dir := util.GetHtmlPath()
|
||||||
|
|
||||||
@ -180,10 +180,10 @@ func (this *TankRouter) ServeHTTP(writer http.ResponseWriter, request *http.Requ
|
|||||||
}
|
}
|
||||||
|
|
||||||
filePath := dir + requestURI
|
filePath := dir + requestURI
|
||||||
exists, _ := util.PathExists(filePath)
|
exists := util.PathExists(filePath)
|
||||||
if !exists {
|
if !exists {
|
||||||
filePath = dir + "/index.html"
|
filePath = dir + "/index.html"
|
||||||
exists, _ = util.PathExists(filePath)
|
exists = util.PathExists(filePath)
|
||||||
if !exists {
|
if !exists {
|
||||||
panic(fmt.Sprintf("404 not found:%s", filePath))
|
panic(fmt.Sprintf("404 not found:%s", filePath))
|
||||||
}
|
}
|
||||||
|
@ -50,3 +50,10 @@ func TestDayAgo(t *testing.T) {
|
|||||||
fmt.Printf("%s\n", util.ConvertTimeToDateTimeString(thenDay))
|
fmt.Printf("%s\n", util.ConvertTimeToDateTimeString(thenDay))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//测试 打包
|
||||||
|
func TestZip(t *testing.T) {
|
||||||
|
|
||||||
|
util.Zip("/Users/fusu/d/group/eyeblue/tank/tmp/matter/admin/root/morning", "/Users/fusu/d/group/eyeblue/tank/tmp/log/morning.zip")
|
||||||
|
|
||||||
|
}
|
||||||
|
95
code/tool/util/util.zip.go
Normal file
95
code/tool/util/util.zip.go
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
package util
|
||||||
|
|
||||||
|
import (
|
||||||
|
"archive/zip"
|
||||||
|
"fmt"
|
||||||
|
"github.com/eyebluecn/tank/code/tool/result"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Zip(srcPath string, destPath string) {
|
||||||
|
|
||||||
|
if PathExists(destPath) {
|
||||||
|
panic(result.BadRequest("%s 已经存在了", destPath))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建准备写入的文件
|
||||||
|
fileWriter, err := os.Create(destPath)
|
||||||
|
PanicError(err)
|
||||||
|
defer func() {
|
||||||
|
err := fileWriter.Close()
|
||||||
|
PanicError(err)
|
||||||
|
}()
|
||||||
|
|
||||||
|
// 通过 fileWriter 来创建 zip.Write
|
||||||
|
zipWriter := zip.NewWriter(fileWriter)
|
||||||
|
defer func() {
|
||||||
|
// 检测一下是否成功关闭
|
||||||
|
if err := zipWriter.Close(); err != nil {
|
||||||
|
log.Fatalln(err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// 下面来将文件写入 zipWriter ,因为有可能会有很多个目录及文件,所以递归处理
|
||||||
|
err = filepath.Walk(srcPath, func(path string, fileInfo os.FileInfo, errBack error) (err error) {
|
||||||
|
if errBack != nil {
|
||||||
|
return errBack
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("遍历文件: " + path)
|
||||||
|
|
||||||
|
// 通过文件信息,创建 zip 的文件信息
|
||||||
|
fileHeader, err := zip.FileInfoHeader(fileInfo)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 替换文件信息中的文件名
|
||||||
|
fileHeader.Name = strings.TrimPrefix(path, string(filepath.Separator))
|
||||||
|
|
||||||
|
// 目录加上/
|
||||||
|
if fileInfo.IsDir() {
|
||||||
|
fileHeader.Name += "/"
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("头部情况: " + fileHeader.Name)
|
||||||
|
|
||||||
|
// 写入文件信息,并返回一个 Write 结构
|
||||||
|
writer, err := zipWriter.CreateHeader(fileHeader)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检测,如果不是标准文件就只写入头信息,不写入文件数据到 writer
|
||||||
|
// 如目录,也没有数据需要写
|
||||||
|
if !fileHeader.Mode().IsRegular() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 打开要压缩的文件
|
||||||
|
fileToBeZip, err := os.Open(path)
|
||||||
|
defer func() {
|
||||||
|
err = fileToBeZip.Close()
|
||||||
|
PanicError(err)
|
||||||
|
}()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将打开的文件 Copy 到 writer
|
||||||
|
_, err = io.Copy(writer, fileToBeZip)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 输出压缩的内容
|
||||||
|
//fmt.Printf("成功压缩文件: %s, 共写入了 %d 个字符的数据\n", path, n)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
PanicError(err)
|
||||||
|
}
|
46
code/tool/util/util_env.go
Normal file
46
code/tool/util/util_env.go
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
package util
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"os/user"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
//是否为win开发环境
|
||||||
|
func EnvWinDevelopment() bool {
|
||||||
|
|
||||||
|
ex, err := os.Executable()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
//如果exPath中包含了 \\AppData\\Local\\Temp 我们认为是在Win的开发环境中
|
||||||
|
systemUser, err := user.Current()
|
||||||
|
if systemUser != nil {
|
||||||
|
|
||||||
|
return strings.HasPrefix(ex, systemUser.HomeDir+"\\AppData\\Local\\Temp")
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//是否为mac开发环境
|
||||||
|
func EnvMacDevelopment() bool {
|
||||||
|
|
||||||
|
ex, err := os.Executable()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return strings.HasPrefix(ex, "/private/var/folders")
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//是否为开发环境 (即是否在IDE中运行)
|
||||||
|
func EnvDevelopment() bool {
|
||||||
|
|
||||||
|
return EnvWinDevelopment() || EnvMacDevelopment()
|
||||||
|
|
||||||
|
}
|
@ -7,22 +7,23 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"os/user"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
//判断文件或文件夹是否已经存在
|
//判断文件或文件夹是否已经存在
|
||||||
func PathExists(path string) (bool, error) {
|
func PathExists(path string) bool {
|
||||||
_, err := os.Stat(path)
|
_, err := os.Stat(path)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return true, nil
|
return true
|
||||||
}
|
} else {
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
return false, nil
|
return false
|
||||||
|
} else {
|
||||||
|
panic(result.BadRequest(err.Error()))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return false, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//获取GOPATH路径
|
//获取GOPATH路径
|
||||||
@ -58,22 +59,13 @@ func GetHomePath() string {
|
|||||||
}
|
}
|
||||||
exPath := filepath.Dir(ex)
|
exPath := filepath.Dir(ex)
|
||||||
|
|
||||||
GetDevHomePath()
|
if EnvMacDevelopment() {
|
||||||
|
|
||||||
//如果exPath中包含了 /private/var/folders 我们认为是在Mac的开发环境中
|
|
||||||
macDev := strings.HasPrefix(exPath, "/private/var/folders")
|
|
||||||
if macDev {
|
|
||||||
exPath = GetDevHomePath() + "/tmp"
|
exPath = GetDevHomePath() + "/tmp"
|
||||||
}
|
}
|
||||||
|
|
||||||
//如果exPath中包含了 \\AppData\\Local\\Temp 我们认为是在Win的开发环境中
|
if EnvWinDevelopment() {
|
||||||
systemUser, err := user.Current()
|
|
||||||
if systemUser != nil {
|
|
||||||
winDev := strings.HasPrefix(exPath, systemUser.HomeDir+"\\AppData\\Local\\Temp")
|
|
||||||
if winDev {
|
|
||||||
exPath = GetDevHomePath() + "/tmp"
|
exPath = GetDevHomePath() + "/tmp"
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return exPath
|
return exPath
|
||||||
}
|
}
|
||||||
@ -84,12 +76,10 @@ func GetHtmlPath() string {
|
|||||||
|
|
||||||
homePath := GetHomePath()
|
homePath := GetHomePath()
|
||||||
filePath := homePath + "/html"
|
filePath := homePath + "/html"
|
||||||
exists, err := PathExists(filePath)
|
exists := PathExists(filePath)
|
||||||
if err != nil {
|
|
||||||
panic("判断上传文件是否存在时出错!")
|
|
||||||
}
|
|
||||||
if !exists {
|
if !exists {
|
||||||
err = os.MkdirAll(filePath, 0777)
|
err := os.MkdirAll(filePath, 0777)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic("创建上传文件夹时出错!")
|
panic("创建上传文件夹时出错!")
|
||||||
}
|
}
|
||||||
@ -101,13 +91,11 @@ func GetHtmlPath() string {
|
|||||||
//如果文件夹存在就不管,不存在就创建。 例如:/var/www/matter
|
//如果文件夹存在就不管,不存在就创建。 例如:/var/www/matter
|
||||||
func MakeDirAll(dirPath string) string {
|
func MakeDirAll(dirPath string) string {
|
||||||
|
|
||||||
exists, err := PathExists(dirPath)
|
exists := PathExists(dirPath)
|
||||||
if err != nil {
|
|
||||||
panic("判断文件是否存在时出错!")
|
|
||||||
}
|
|
||||||
if !exists {
|
if !exists {
|
||||||
//TODO:文件权限需要进一步考虑
|
//TODO:文件权限需要进一步考虑
|
||||||
err = os.MkdirAll(dirPath, 0777)
|
err := os.MkdirAll(dirPath, 0777)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic("创建文件夹时出错!")
|
panic("创建文件夹时出错!")
|
||||||
}
|
}
|
||||||
@ -178,16 +166,13 @@ func DeleteEmptyDirRecursive(dirPath string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//移除某个文件夹。 例如:/var/www/matter => /var/www
|
//移除某个文件夹。
|
||||||
func RemoveDirectory(dirPath string) string {
|
func RemoveDirectory(dirPath string) string {
|
||||||
|
|
||||||
exists, err := PathExists(dirPath)
|
exists := PathExists(dirPath)
|
||||||
if err != nil {
|
|
||||||
panic("判断文件是否存在时出错!")
|
|
||||||
}
|
|
||||||
if exists {
|
if exists {
|
||||||
|
|
||||||
err = os.Remove(dirPath)
|
err := os.Remove(dirPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic("删除文件夹时出错!")
|
panic("删除文件夹时出错!")
|
||||||
}
|
}
|
||||||
@ -202,12 +187,10 @@ func GetConfPath() string {
|
|||||||
|
|
||||||
homePath := GetHomePath()
|
homePath := GetHomePath()
|
||||||
filePath := homePath + "/conf"
|
filePath := homePath + "/conf"
|
||||||
exists, err := PathExists(filePath)
|
exists := PathExists(filePath)
|
||||||
if err != nil {
|
|
||||||
panic("判断日志文件夹是否存在时出错!")
|
|
||||||
}
|
|
||||||
if !exists {
|
if !exists {
|
||||||
err = os.MkdirAll(filePath, 0777)
|
err := os.MkdirAll(filePath, 0777)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic("创建日志文件夹时出错!")
|
panic("创建日志文件夹时出错!")
|
||||||
}
|
}
|
||||||
@ -222,12 +205,10 @@ func GetLogPath() string {
|
|||||||
|
|
||||||
homePath := GetHomePath()
|
homePath := GetHomePath()
|
||||||
filePath := homePath + "/log"
|
filePath := homePath + "/log"
|
||||||
exists, err := PathExists(filePath)
|
exists := PathExists(filePath)
|
||||||
if err != nil {
|
|
||||||
panic("判断日志文件夹是否存在时出错!")
|
|
||||||
}
|
|
||||||
if !exists {
|
if !exists {
|
||||||
err = os.MkdirAll(filePath, 0777)
|
err := os.MkdirAll(filePath, 0777)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic("创建日志文件夹时出错!")
|
panic("创建日志文件夹时出错!")
|
||||||
}
|
}
|
||||||
|
1
go.sum
1
go.sum
@ -159,6 +159,7 @@ golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5h
|
|||||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20181218192612-074acd46bca6/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20181218192612-074acd46bca6/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
15
main.go
15
main.go
@ -11,24 +11,31 @@ import (
|
|||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
||||||
//第一步。日志
|
//第一步。命令行工具处理
|
||||||
|
tankCommand := &support.TankCommand{}
|
||||||
|
core.COMMAND = tankCommand
|
||||||
|
if core.COMMAND.Cli() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//第二步。日志
|
||||||
tankLogger := &support.TankLogger{}
|
tankLogger := &support.TankLogger{}
|
||||||
core.LOGGER = tankLogger
|
core.LOGGER = tankLogger
|
||||||
tankLogger.Init()
|
tankLogger.Init()
|
||||||
defer tankLogger.Destroy()
|
defer tankLogger.Destroy()
|
||||||
|
|
||||||
//第二步。配置
|
//第三步。配置
|
||||||
tankConfig := &support.TankConfig{}
|
tankConfig := &support.TankConfig{}
|
||||||
core.CONFIG = tankConfig
|
core.CONFIG = tankConfig
|
||||||
tankConfig.Init()
|
tankConfig.Init()
|
||||||
|
|
||||||
//第三步。全局运行的上下文
|
//第四步。全局运行的上下文
|
||||||
tankContext := &support.TankContext{}
|
tankContext := &support.TankContext{}
|
||||||
core.CONTEXT = tankContext
|
core.CONTEXT = tankContext
|
||||||
tankContext.Init()
|
tankContext.Init()
|
||||||
defer tankContext.Destroy()
|
defer tankContext.Destroy()
|
||||||
|
|
||||||
//第四步。启动http服务
|
//第五步。启动http服务
|
||||||
http.Handle("/", core.CONTEXT)
|
http.Handle("/", core.CONTEXT)
|
||||||
core.LOGGER.Info("App started at http://localhost:%v", core.CONFIG.ServerPort())
|
core.LOGGER.Info("App started at http://localhost:%v", core.CONFIG.ServerPort())
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user