diff --git a/models/file.go b/models/file.go
index 21f38da..7487325 100644
--- a/models/file.go
+++ b/models/file.go
@@ -161,3 +161,8 @@ func (file *File) Rename(new string) error {
 func (file *File) UpdatePicInfo(value string) error {
 	return DB.Model(&file).Update("pic_info", value).Error
 }
+
+// UpdatePicInfo 更新文件的图像信息
+func (file *File) UpdateSize(value uint64) error {
+	return DB.Model(&file).Update("size", value).Error
+}
diff --git a/pkg/filesystem/hooks.go b/pkg/filesystem/hooks.go
index a509efd..b1d3365 100644
--- a/pkg/filesystem/hooks.go
+++ b/pkg/filesystem/hooks.go
@@ -3,8 +3,12 @@ package filesystem
 import (
 	"context"
 	"errors"
+	model "github.com/HFO4/cloudreve/models"
+	"github.com/HFO4/cloudreve/pkg/conf"
 	"github.com/HFO4/cloudreve/pkg/filesystem/fsctx"
 	"github.com/HFO4/cloudreve/pkg/util"
+	"io/ioutil"
+	"strings"
 )
 
 // Hook 钩子函数
@@ -71,6 +75,17 @@ func HookValidateFile(ctx context.Context, fs *FileSystem) error {
 
 }
 
+// HookResetPolicy 重设存储策略为已有文件
+func HookResetPolicy(ctx context.Context, fs *FileSystem) error {
+	originFile, ok := ctx.Value(fsctx.FileModelCtx).(model.File)
+	if !ok {
+		return ErrObjectNotExist
+	}
+
+	fs.Policy = originFile.GetPolicy()
+	return fs.dispatchHandler()
+}
+
 // HookValidateCapacity 验证并扣除用户容量,包含数据库操作
 func HookValidateCapacity(ctx context.Context, fs *FileSystem) error {
 	file := ctx.Value(fsctx.FileHeaderCtx).(FileHeader)
@@ -81,10 +96,27 @@ func HookValidateCapacity(ctx context.Context, fs *FileSystem) error {
 	return nil
 }
 
+// HookChangeCapacity 根据原有文件和新文件的大小更新用户容量
+func HookChangeCapacity(ctx context.Context, fs *FileSystem) error {
+	newFile := ctx.Value(fsctx.FileHeaderCtx).(FileHeader)
+	originFile := ctx.Value(fsctx.FileModelCtx).(model.File)
+
+	if newFile.GetSize() > originFile.Size {
+		if !fs.ValidateCapacity(ctx, newFile.GetSize()-originFile.Size) {
+			return ErrInsufficientCapacity
+		}
+		return nil
+	}
+
+	fs.User.DeductionStorage(originFile.Size - newFile.GetSize())
+	return nil
+}
+
 // HookDeleteTempFile 删除已保存的临时文件
 func HookDeleteTempFile(ctx context.Context, fs *FileSystem) error {
 	filePath := ctx.Value(fsctx.SavePathCtx).(string)
 	// 删除临时文件
+	// TODO 其他策略。Exists?
 	if util.Exists(filePath) {
 		_, err := fs.Handler.Delete(ctx, []string{filePath})
 		if err != nil {
@@ -95,6 +127,22 @@ func HookDeleteTempFile(ctx context.Context, fs *FileSystem) error {
 	return nil
 }
 
+// HookCleanFileContent 清空文件内容
+func HookCleanFileContent(ctx context.Context, fs *FileSystem) error {
+	filePath := ctx.Value(fsctx.SavePathCtx).(string)
+	// 清空内容
+	return fs.Handler.Put(ctx, ioutil.NopCloser(strings.NewReader("")), filePath, 0)
+}
+
+// HookClearFileSize 将原始文件的尺寸设为0
+func HookClearFileSize(ctx context.Context, fs *FileSystem) error {
+	originFile, ok := ctx.Value(fsctx.FileModelCtx).(model.File)
+	if !ok {
+		return ErrObjectNotExist
+	}
+	return originFile.UpdateSize(0)
+}
+
 // HookGiveBackCapacity 归还用户容量
 func HookGiveBackCapacity(ctx context.Context, fs *FileSystem) error {
 	file := ctx.Value(fsctx.FileHeaderCtx).(FileHeader)
@@ -106,6 +154,34 @@ func HookGiveBackCapacity(ctx context.Context, fs *FileSystem) error {
 	return nil
 }
 
+// GenericAfterUpdate 文件内容更新后
+func GenericAfterUpdate(ctx context.Context, fs *FileSystem) error {
+	// 更新文件尺寸
+	originFile, ok := ctx.Value(fsctx.FileModelCtx).(model.File)
+	if !ok {
+		return ErrObjectNotExist
+	}
+	newFile, ok := ctx.Value(fsctx.FileHeaderCtx).(FileHeader)
+	if !ok {
+		return ErrObjectNotExist
+	}
+	err := originFile.UpdateSize(newFile.GetSize())
+	if err != nil {
+		return err
+	}
+
+	// 尝试清空原有缩略图
+	go func() {
+		if originFile.PicInfo != "" {
+			_, _ = fs.Handler.Delete(ctx, []string{originFile.SourceName + conf.ThumbConfig.FileSuffix})
+			fs.GenerateThumbnail(ctx, &originFile)
+		}
+
+	}()
+
+	return nil
+}
+
 // GenericAfterUpload 文件上传完成后,包含数据库操作
 func GenericAfterUpload(ctx context.Context, fs *FileSystem) error {
 	// 文件存放的虚拟路径
diff --git a/pkg/filesystem/upload.go b/pkg/filesystem/upload.go
index c066c82..39ac3c2 100644
--- a/pkg/filesystem/upload.go
+++ b/pkg/filesystem/upload.go
@@ -2,6 +2,7 @@ package filesystem
 
 import (
 	"context"
+	model "github.com/HFO4/cloudreve/models"
 	"github.com/HFO4/cloudreve/pkg/filesystem/fsctx"
 	"github.com/HFO4/cloudreve/pkg/util"
 	"github.com/gin-gonic/gin"
@@ -23,8 +24,14 @@ func (fs *FileSystem) Upload(ctx context.Context, file FileHeader) (err error) {
 		return err
 	}
 
-	// 生成文件名和路径
-	savePath := fs.GenerateSavePath(ctx, file)
+	// 生成文件名和路径, 如果是更新操作就从原始文件获取
+	var savePath string
+	originFile, ok := ctx.Value(fsctx.FileModelCtx).(model.File)
+	if ok {
+		savePath = originFile.SourceName
+	} else {
+		savePath = fs.GenerateSavePath(ctx, file)
+	}
 
 	// 处理客户端未完成上传时,关闭连接
 	go fs.CancelUpload(ctx, savePath, file)
@@ -50,7 +57,7 @@ func (fs *FileSystem) Upload(ctx context.Context, file FileHeader) (err error) {
 		return err
 	}
 
-	util.Log().Info("新文件上传:%s , 大小:%d, 上传者:%s", file.GetFileName(), file.GetSize(), fs.User.Nick)
+	util.Log().Info("新文件PUT:%s , 大小:%d, 上传者:%s", file.GetFileName(), file.GetSize(), fs.User.Nick)
 
 	return nil
 }
diff --git a/routers/controllers/file.go b/routers/controllers/file.go
index 79ae686..32c8957 100644
--- a/routers/controllers/file.go
+++ b/routers/controllers/file.go
@@ -143,7 +143,7 @@ func Preview(c *gin.Context) {
 	ctx, cancel := context.WithCancel(context.Background())
 	defer cancel()
 
-	var service explorer.FileDownloadCreateService
+	var service explorer.SingleFileService
 	if err := c.ShouldBindUri(&service); err == nil {
 		res := service.PreviewContent(ctx, c)
 		// 是否需要重定向
@@ -164,7 +164,7 @@ func CreateDownloadSession(c *gin.Context) {
 	ctx, cancel := context.WithCancel(context.Background())
 	defer cancel()
 
-	var service explorer.FileDownloadCreateService
+	var service explorer.SingleFileService
 	if err := c.ShouldBindUri(&service); err == nil {
 		res := service.CreateDownloadSession(ctx, c)
 		c.JSON(200, res)
@@ -190,6 +190,21 @@ func Download(c *gin.Context) {
 	}
 }
 
+// PutContent 更新文件内容
+func PutContent(c *gin.Context) {
+	// 创建上下文
+	ctx, cancel := context.WithCancel(context.Background())
+	defer cancel()
+
+	var service explorer.SingleFileService
+	if err := c.ShouldBindUri(&service); err == nil {
+		res := service.PutContent(ctx, c)
+		c.JSON(200, res)
+	} else {
+		c.JSON(200, ErrorResponse(err))
+	}
+}
+
 // FileUploadStream 本地策略流式上传
 func FileUploadStream(c *gin.Context) {
 	// 创建上下文
@@ -251,6 +266,5 @@ func FileUploadStream(c *gin.Context) {
 
 	c.JSON(200, serializer.Response{
 		Code: 0,
-		Msg:  "Pong",
 	})
 }
diff --git a/routers/router.go b/routers/router.go
index ccea9cf..052fdcb 100644
--- a/routers/router.go
+++ b/routers/router.go
@@ -104,6 +104,8 @@ func InitRouter() *gin.Engine {
 			{
 				// 文件上传
 				file.POST("upload", controllers.FileUploadStream)
+				// 更新文件
+				file.PUT("update/*path", controllers.PutContent)
 				// 创建文件下载会话
 				file.PUT("download/*path", controllers.CreateDownloadSession)
 				// 预览文件
diff --git a/service/explorer/file.go b/service/explorer/file.go
index f68654d..6d1448d 100644
--- a/service/explorer/file.go
+++ b/service/explorer/file.go
@@ -6,14 +6,17 @@ import (
 	"github.com/HFO4/cloudreve/pkg/cache"
 	"github.com/HFO4/cloudreve/pkg/filesystem"
 	"github.com/HFO4/cloudreve/pkg/filesystem/fsctx"
+	"github.com/HFO4/cloudreve/pkg/filesystem/local"
 	"github.com/HFO4/cloudreve/pkg/serializer"
 	"github.com/gin-gonic/gin"
 	"net/http"
+	"path"
+	"strconv"
 	"time"
 )
 
-// FileDownloadCreateService 文件下载会话创建服务,path为文件完整路径
-type FileDownloadCreateService struct {
+// SingleFileService 对单文件进行操作的五福,path为文件完整路径
+type SingleFileService struct {
 	Path string `uri:"path" binding:"required,min=1,max=65535"`
 }
 
@@ -93,7 +96,7 @@ func (service *FileAnonymousGetService) Download(ctx context.Context, c *gin.Con
 }
 
 // CreateDownloadSession 创建下载会话,获取下载URL
-func (service *FileDownloadCreateService) CreateDownloadSession(ctx context.Context, c *gin.Context) serializer.Response {
+func (service *SingleFileService) CreateDownloadSession(ctx context.Context, c *gin.Context) serializer.Response {
 	// 创建文件系统
 	fs, err := filesystem.NewFileSystemFromContext(c)
 	if err != nil {
@@ -152,7 +155,7 @@ func (service *DownloadService) Download(ctx context.Context, c *gin.Context) se
 }
 
 // PreviewContent 预览文件,需要登录会话
-func (service *FileDownloadCreateService) PreviewContent(ctx context.Context, c *gin.Context) serializer.Response {
+func (service *SingleFileService) PreviewContent(ctx context.Context, c *gin.Context) serializer.Response {
 	// 创建文件系统
 	fs, err := filesystem.NewFileSystemFromContext(c)
 	if err != nil {
@@ -172,3 +175,61 @@ func (service *FileDownloadCreateService) PreviewContent(ctx context.Context, c
 		Code: 0,
 	}
 }
+
+// PutContent 更新文件内容
+func (service *SingleFileService) PutContent(ctx context.Context, c *gin.Context) serializer.Response {
+	// 创建上下文
+	ctx, cancel := context.WithCancel(context.Background())
+	defer cancel()
+
+	// 取得文件大小
+	fileSize, err := strconv.ParseUint(c.Request.Header.Get("Content-Length"), 10, 64)
+	if err != nil {
+
+		return serializer.ParamErr("无法解析文件尺寸", err)
+	}
+
+	fileData := local.FileStream{
+		MIMEType:    c.Request.Header.Get("Content-Type"),
+		File:        c.Request.Body,
+		Size:        fileSize,
+		Name:        path.Base(service.Path),
+		VirtualPath: path.Dir(service.Path),
+	}
+
+	// 创建文件系统
+	fs, err := filesystem.NewFileSystemFromContext(c)
+	if err != nil {
+		return serializer.Err(serializer.CodePolicyNotAllowed, err.Error(), err)
+	}
+
+	// 取得现有文件
+	exist, originFile := fs.IsFileExist(service.Path)
+	if !exist {
+		return serializer.Err(404, "文件不存在", nil)
+	}
+
+	// 给文件系统分配钩子
+	fs.Use("BeforeUpload", filesystem.HookValidateFile)
+	fs.Use("BeforeUpload", filesystem.HookResetPolicy)
+	fs.Use("BeforeUpload", filesystem.HookChangeCapacity)
+	fs.Use("AfterUploadCanceled", filesystem.HookCleanFileContent)
+	fs.Use("AfterUploadCanceled", filesystem.HookClearFileSize)
+	fs.Use("AfterUploadCanceled", filesystem.HookGiveBackCapacity)
+	fs.Use("AfterUpload", filesystem.GenericAfterUpdate)
+	fs.Use("AfterValidateFailed", filesystem.HookCleanFileContent)
+	fs.Use("AfterValidateFailed", filesystem.HookClearFileSize)
+	fs.Use("AfterValidateFailed", filesystem.HookGiveBackCapacity)
+
+	// 执行上传
+	uploadCtx := context.WithValue(ctx, fsctx.GinCtx, c)
+	uploadCtx = context.WithValue(uploadCtx, fsctx.FileModelCtx, *originFile)
+	err = fs.Upload(uploadCtx, fileData)
+	if err != nil {
+		return serializer.Err(serializer.CodeUploadFailed, err.Error(), err)
+	}
+
+	return serializer.Response{
+		Code: 0,
+	}
+}