diff --git a/models/file.go b/models/file.go
index 05ce596..2645662 100644
--- a/models/file.go
+++ b/models/file.go
@@ -1,6 +1,9 @@
 package model
 
-import "github.com/jinzhu/gorm"
+import (
+	"github.com/HFO4/cloudreve/pkg/util"
+	"github.com/jinzhu/gorm"
+)
 
 // File 文件
 type File struct {
@@ -19,6 +22,7 @@ type File struct {
 // Create 创建文件记录
 func (file *File) Create() (uint, error) {
 	if err := DB.Create(file).Error; err != nil {
+		util.Log().Warning("无法插入文件记录, %s", err)
 		return 0, err
 	}
 	return file.ID, nil
diff --git a/pkg/filesystem/file.go b/pkg/filesystem/file.go
new file mode 100644
index 0000000..7f43778
--- /dev/null
+++ b/pkg/filesystem/file.go
@@ -0,0 +1,34 @@
+package filesystem
+
+import (
+	"context"
+	model "github.com/HFO4/cloudreve/models"
+)
+
+/* ============
+	 文件相关
+   ============
+*/
+
+// AddFile 新增文件记录
+func (fs *FileSystem) AddFile(ctx context.Context, parent *model.Folder) (*model.File, error) {
+	file := ctx.Value(FileHeaderCtx).(FileHeader)
+	filePath := ctx.Value(SavePathCtx).(string)
+
+	newFile := model.File{
+		Name:       file.GetFileName(),
+		SourceName: filePath,
+		UserID:     fs.User.ID,
+		Size:       file.GetSize(),
+		FolderID:   parent.ID,
+		PolicyID:   fs.User.Policy.ID,
+		Dir:        parent.PositionAbsolute,
+	}
+	_, err := newFile.Create()
+
+	if err != nil {
+		return nil, err
+	}
+
+	return &newFile, nil
+}
diff --git a/pkg/filesystem/filesystem.go b/pkg/filesystem/filesystem.go
index 6049adb..1abe449 100644
--- a/pkg/filesystem/filesystem.go
+++ b/pkg/filesystem/filesystem.go
@@ -4,11 +4,7 @@ import (
 	"context"
 	"github.com/HFO4/cloudreve/models"
 	"github.com/HFO4/cloudreve/pkg/filesystem/local"
-	"github.com/HFO4/cloudreve/pkg/util"
-	"github.com/gin-gonic/gin"
 	"io"
-	"path"
-	"path/filepath"
 )
 
 // FileHeader 上传来的文件数据处理器
@@ -32,12 +28,12 @@ type Handler interface {
 // FileSystem 管理文件的文件系统
 type FileSystem struct {
 	/*
-	  文件系统所有者
+	   文件系统所有者
 	*/
 	User *model.User
 
 	/*
-	  钩子函数
+	   钩子函数
 	*/
 	// 上传文件前
 	BeforeUpload func(ctx context.Context, fs *FileSystem) error
@@ -49,7 +45,7 @@ type FileSystem struct {
 	AfterUploadCanceled func(ctx context.Context, fs *FileSystem) error
 
 	/*
-	  文件系统处理适配器
+	   文件系统处理适配器
 	*/
 	Handler Handler
 }
@@ -72,136 +68,3 @@ func NewFileSystem(user *model.User) (*FileSystem, error) {
 		Handler: handler,
 	}, nil
 }
-
-/* ============
-	 文件相关
-   ============
-*/
-
-// AddFile 新增文件记录
-func (fs *FileSystem) AddFile(ctx context.Context, parent *model.Folder) (*model.File, error) {
-	file := ctx.Value(FileHeaderCtx).(FileHeader)
-	filePath := ctx.Value(SavePathCtx).(string)
-
-	newFile := model.File{
-		Name:       file.GetFileName(),
-		SourceName: filePath,
-		UserID:     fs.User.ID,
-		Size:       file.GetSize(),
-		FolderID:   parent.ID,
-		PolicyID:   fs.User.Policy.ID,
-		Dir:        parent.PositionAbsolute,
-	}
-	_, err := newFile.Create()
-
-	if err != nil {
-		return nil, err
-	}
-
-	return &newFile, nil
-}
-
-/* ================
-	 上传处理相关
-   ================
-*/
-
-// Upload 上传文件
-func (fs *FileSystem) Upload(ctx context.Context, file FileHeader) (err error) {
-	ctx = context.WithValue(ctx, FileHeaderCtx, file)
-
-	// 上传前的钩子
-	if fs.BeforeUpload != nil {
-		err = fs.BeforeUpload(ctx, fs)
-		if err != nil {
-			return err
-		}
-	}
-
-	// 生成文件名和路径
-	savePath := fs.GenerateSavePath(ctx, file)
-
-	// 处理客户端未完成上传时,关闭连接
-	go fs.CancelUpload(ctx, savePath, file)
-
-	// 保存文件
-	err = fs.Handler.Put(ctx, file, savePath)
-	if err != nil {
-		return err
-	}
-
-	// 上传完成后的钩子
-	if fs.AfterUpload != nil {
-		ctx = context.WithValue(ctx, SavePathCtx, savePath)
-		err = fs.AfterUpload(ctx, fs)
-
-		if err != nil {
-			// 上传完成后续处理失败
-			if fs.AfterValidateFailed != nil {
-				followUpErr := fs.AfterValidateFailed(ctx, fs)
-				// 失败后再失败...
-				if followUpErr != nil {
-					util.Log().Warning("AfterValidateFailed 钩子执行失败,%s", followUpErr)
-				}
-			}
-			return err
-		}
-	}
-
-	return nil
-}
-
-// GenerateSavePath 生成要存放文件的路径
-func (fs *FileSystem) GenerateSavePath(ctx context.Context, file FileHeader) string {
-	return filepath.Join(
-		fs.User.Policy.GeneratePath(
-			fs.User.Model.ID,
-			file.GetVirtualPath(),
-		),
-		fs.User.Policy.GenerateFileName(
-			fs.User.Model.ID,
-			file.GetFileName(),
-		),
-	)
-}
-
-// CancelUpload 监测客户端取消上传
-func (fs *FileSystem) CancelUpload(ctx context.Context, path string, file FileHeader) {
-	ginCtx := ctx.Value(GinCtx).(*gin.Context)
-	select {
-	case <-ctx.Done():
-		// 客户端正常关闭,不执行操作
-	case <-ginCtx.Request.Context().Done():
-		// 客户端取消了上传
-		if fs.AfterUploadCanceled == nil {
-			return
-		}
-		ctx = context.WithValue(ctx, SavePathCtx, path)
-		err := fs.AfterUploadCanceled(ctx, fs)
-		if err != nil {
-			util.Log().Warning("执行 AfterUploadCanceled 钩子出错,%s", err)
-		}
-	}
-}
-
-/* =================
-	 路径/目录相关
-   =================
-*/
-
-// IsPathExist 返回给定目录是否存在
-// 如果存在就返回目录
-func (fs *FileSystem) IsPathExist(path string) (bool, model.Folder) {
-	folder, err := model.GetFolderByPath(path, fs.User.ID)
-	return err == nil, folder
-}
-
-// IsFileExist 返回给定路径的文件是否存在
-func (fs *FileSystem) IsFileExist(fullPath string) bool {
-	basePath := path.Dir(fullPath)
-	fileName := path.Base(fullPath)
-
-	_, err := model.GetFileByPathAndName(basePath, fileName, fs.User.ID)
-
-	return err == nil
-}
diff --git a/pkg/filesystem/hooks.go b/pkg/filesystem/hooks.go
index 105da3c..194b16b 100644
--- a/pkg/filesystem/hooks.go
+++ b/pkg/filesystem/hooks.go
@@ -73,12 +73,15 @@ func GenericAfterUpload(ctx context.Context, fs *FileSystem) error {
 	}
 
 	// 向数据库中插入记录
-	_, err := fs.AddFile(ctx, &folder)
+	file, err := fs.AddFile(ctx, &folder)
 	if err != nil {
 		return errors.New("无法插入文件记录")
 	}
 
+	// TODO 是否需要立即获取图像大小?
+
 	// 异步尝试生成缩略图
+	go fs.GenerateThumbnail(ctx, file)
 
 	return nil
 }
diff --git a/pkg/filesystem/image.go b/pkg/filesystem/image.go
new file mode 100644
index 0000000..412784f
--- /dev/null
+++ b/pkg/filesystem/image.go
@@ -0,0 +1,19 @@
+package filesystem
+
+import (
+	"context"
+	model "github.com/HFO4/cloudreve/models"
+)
+
+/* ===============
+	图像处理相关
+   ===============
+*/
+
+// HandledExtension 可以生成缩略图的文件扩展名
+var HandledExtension = []string{}
+
+// GenerateThumbnail 尝试为文件生成缩略图
+func (fs *FileSystem) GenerateThumbnail(ctx context.Context, file *model.File) {
+	// TODO
+}
diff --git a/pkg/filesystem/path.go b/pkg/filesystem/path.go
new file mode 100644
index 0000000..deb2f65
--- /dev/null
+++ b/pkg/filesystem/path.go
@@ -0,0 +1,28 @@
+package filesystem
+
+import (
+	model "github.com/HFO4/cloudreve/models"
+	"path"
+)
+
+/* =================
+	 路径/目录相关
+   =================
+*/
+
+// IsPathExist 返回给定目录是否存在
+// 如果存在就返回目录
+func (fs *FileSystem) IsPathExist(path string) (bool, model.Folder) {
+	folder, err := model.GetFolderByPath(path, fs.User.ID)
+	return err == nil, folder
+}
+
+// IsFileExist 返回给定路径的文件是否存在
+func (fs *FileSystem) IsFileExist(fullPath string) bool {
+	basePath := path.Dir(fullPath)
+	fileName := path.Base(fullPath)
+
+	_, err := model.GetFileByPathAndName(basePath, fileName, fs.User.ID)
+
+	return err == nil
+}
diff --git a/pkg/filesystem/upload.go b/pkg/filesystem/upload.go
new file mode 100644
index 0000000..134ad4e
--- /dev/null
+++ b/pkg/filesystem/upload.go
@@ -0,0 +1,93 @@
+package filesystem
+
+import (
+	"context"
+	"github.com/HFO4/cloudreve/pkg/util"
+	"github.com/gin-gonic/gin"
+	"path/filepath"
+)
+
+/* ================
+	 上传处理相关
+   ================
+*/
+
+// Upload 上传文件
+func (fs *FileSystem) Upload(ctx context.Context, file FileHeader) (err error) {
+	ctx = context.WithValue(ctx, FileHeaderCtx, file)
+
+	// 上传前的钩子
+	if fs.BeforeUpload != nil {
+		err = fs.BeforeUpload(ctx, fs)
+		if err != nil {
+			return err
+		}
+	}
+
+	// 生成文件名和路径
+	savePath := fs.GenerateSavePath(ctx, file)
+
+	// 处理客户端未完成上传时,关闭连接
+	go fs.CancelUpload(ctx, savePath, file)
+
+	// 保存文件
+	err = fs.Handler.Put(ctx, file, savePath)
+	if err != nil {
+		return err
+	}
+
+	// 上传完成后的钩子
+	if fs.AfterUpload != nil {
+		ctx = context.WithValue(ctx, SavePathCtx, savePath)
+		err = fs.AfterUpload(ctx, fs)
+
+		if err != nil {
+			// 上传完成后续处理失败
+			if fs.AfterValidateFailed != nil {
+				followUpErr := fs.AfterValidateFailed(ctx, fs)
+				// 失败后再失败...
+				if followUpErr != nil {
+					util.Log().Warning("AfterValidateFailed 钩子执行失败,%s", followUpErr)
+				}
+			}
+			return err
+		}
+	}
+
+	util.Log().Info("新文件上传:%s , 大小:%d, 上传者:%s", file.GetFileName(), file.GetSize(), fs.User.Nick)
+
+	return nil
+}
+
+// GenerateSavePath 生成要存放文件的路径
+func (fs *FileSystem) GenerateSavePath(ctx context.Context, file FileHeader) string {
+	return filepath.Join(
+		fs.User.Policy.GeneratePath(
+			fs.User.Model.ID,
+			file.GetVirtualPath(),
+		),
+		fs.User.Policy.GenerateFileName(
+			fs.User.Model.ID,
+			file.GetFileName(),
+		),
+	)
+}
+
+// CancelUpload 监测客户端取消上传
+func (fs *FileSystem) CancelUpload(ctx context.Context, path string, file FileHeader) {
+	ginCtx := ctx.Value(GinCtx).(*gin.Context)
+	select {
+	case <-ctx.Done():
+		// 客户端正常关闭,不执行操作
+	case <-ginCtx.Request.Context().Done():
+		// 客户端取消了上传
+		if fs.AfterUploadCanceled == nil {
+			return
+		}
+		ctx = context.WithValue(ctx, SavePathCtx, path)
+		err := fs.AfterUploadCanceled(ctx, fs)
+		if err != nil {
+			util.Log().Warning("执行 AfterUploadCanceled 钩子出错,%s", err)
+		}
+	}
+}
diff --git a/pkg/filesystem/validator.go b/pkg/filesystem/validator.go
index f749f6c..5ff229c 100644
--- a/pkg/filesystem/validator.go
+++ b/pkg/filesystem/validator.go
@@ -7,6 +7,11 @@ import (
 	"strings"
 )
 
+/* ==========
+	 验证器
+   ==========
+*/
+
 // 文件/路径名保留字符
 var reservedCharacter = []string{"\\", "?", "*", "<", "\"", ":", ">", "/"}