Browse Source

add some interface

tags/v1.21.12.1
yuyuanshifu 5 years ago
parent
commit
076af2ff71
6 changed files with 203 additions and 6 deletions
  1. +17
    -2
      models/error.go
  2. +78
    -0
      models/file_chunk.go
  3. +1
    -0
      models/models.go
  4. +5
    -1
      modules/storage/minio_ext.go
  5. +99
    -3
      routers/repo/attachment.go
  6. +3
    -0
      routers/routes/routes.go

+ 17
- 2
models/error.go View File

@@ -6,9 +6,8 @@
package models package models


import ( import (
"fmt"

"code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/git"
"fmt"
) )


// ErrNotExist represents a non-exist error. // ErrNotExist represents a non-exist error.
@@ -1971,3 +1970,19 @@ func IsErrOAuthApplicationNotFound(err error) bool {
func (err ErrOAuthApplicationNotFound) Error() string { func (err ErrOAuthApplicationNotFound) Error() string {
return fmt.Sprintf("OAuth application not found [ID: %d]", err.ID) return fmt.Sprintf("OAuth application not found [ID: %d]", err.ID)
} }

// ErrFileChunkNotExist represents a "FileChunkNotExist" kind of error.
type ErrFileChunkNotExist struct {
Md5 string
Uuid string
}

func (err ErrFileChunkNotExist) Error() string {
return fmt.Sprintf("fileChunk does not exist [md5: %s, uuid: %s]", err.Md5, err.Uuid)
}

// IsErrFileChunkNotExist checks if an error is a ErrFileChunkNotExist.
func IsErrFileChunkNotExist(err error) bool {
_, ok := err.(ErrFileChunkNotExist)
return ok
}

+ 78
- 0
models/file_chunk.go View File

@@ -0,0 +1,78 @@
package models

import (
"code.gitea.io/gitea/modules/timeutil"
"xorm.io/xorm"
)

const (
FileNotUploaded int = iota
FileUploaded
)

type FileChunk struct {
ID int64 `xorm:"pk autoincr"`
UUID string `xorm:"uuid UNIQUE"`
Md5 string `xorm:"UNIQUE"`
IsUploaded int `xorm:"DEFAULT 0"` // not uploaded: 0, uploaded: 1
HasUploaded string //chunkNumbers 1,2,3
UploadID string `xorm:"UNIQUE"`//minio upload id
TotalChunks int64
Size int64
UserID int64 `xorm:"INDEX"`
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
}

// GetFileChunkByMD5 returns fileChunk by given id
func GetFileChunkByMD5(md5 string) (*FileChunk, error) {
return getFileChunkByMD5(x, md5)
}

func getFileChunkByMD5(e Engine, md5 string) (*FileChunk, error) {
fileChunk := new(FileChunk)

if has, err := e.Where("md5 = ?", md5).Get(fileChunk); err != nil {
return nil, err
} else if !has {
return nil, ErrFileChunkNotExist{md5, ""}
}
return fileChunk, nil
}

// GetAttachmentByID returns attachment by given id
func GetFileChunkByUUID(uuid string) (*FileChunk, error) {
return getFileChunkByUUID(x, uuid)
}

func getFileChunkByUUID(e Engine, uuid string) (*FileChunk, error) {
fileChunk := new(FileChunk)

if has, err := e.Where("uuid = ?", uuid).Get(fileChunk); err != nil {
return nil, err
} else if !has {
return nil, ErrFileChunkNotExist{"", uuid}
}
return fileChunk, nil
}

// InsertFileChunk insert a record into file_chunk.
func InsertFileChunk(fileChunk *FileChunk) (_ *FileChunk, err error) {
if _, err := x.Insert(fileChunk); err != nil {
return nil, err
}

return fileChunk,nil
}

// UpdateAttachment updates the given attachment in database
func UpdateFileChunk(fileChunk *FileChunk) error {
return updateFileChunk(x, fileChunk)
}

func updateFileChunk(e Engine, fileChunk *FileChunk) error {
var sess *xorm.Session
sess = e.Where("uuid = ?", fileChunk.UUID)
_, err := sess.Cols("is_uploaded", "has_uploaded").Update(fileChunk)
return err
}

+ 1
- 0
models/models.go View File

@@ -126,6 +126,7 @@ func init() {
new(LanguageStat), new(LanguageStat),
new(EmailHash), new(EmailHash),
new(Dataset), new(Dataset),
new(FileChunk),
) )


gonicNames := []string{"SSL", "UID"} gonicNames := []string{"SSL", "UID"}


+ 5
- 1
modules/storage/minio_ext.go View File

@@ -98,13 +98,17 @@ func getClients()(*minio_ext.Client, *miniov6.Core, error){
return client, core, nil return client, core, nil
} }


func GenMultiPartSignedUrl(bucketName string, objectName string, uploadId string, partNumber int, partSize int64) (string, error) {
func GenMultiPartSignedUrl(uuid string, uploadId string, partNumber int, partSize int64) (string, error) {
minioClient, _, err := getClients() minioClient, _, err := getClients()
if err != nil { if err != nil {
log.Error("getClients failed:", err.Error()) log.Error("getClients failed:", err.Error())
return "", err return "", err
} }


minio := setting.Attachment.Minio
bucketName := minio.Bucket
objectName := strings.TrimPrefix(path.Join(minio.BasePath, path.Join(uuid[0:1], uuid[1:2], uuid)), "/")

return minioClient.GenUploadPartSignedUrl(uploadId, bucketName, objectName, partNumber, partSize, PresignedUploadPartUrlExpireTime) return minioClient.GenUploadPartSignedUrl(uploadId, bucketName, objectName, partNumber, partSize, PresignedUploadPartUrlExpireTime)


} }


+ 99
- 3
routers/repo/attachment.go View File

@@ -277,6 +277,28 @@ func AddAttachment(ctx *context.Context) {
}) })
} }


func GetSuccessChunks(ctx *context.Context) {
fileMD5 := ctx.Params("fileMD5")

fileChunk, err := models.GetFileChunkByMD5(fileMD5)
if err != nil {
if models.IsErrFileChunkNotExist(err) {
ctx.Error(404)
} else {
ctx.ServerError("GetFileChunkByMD5", err)
}
return
}

ctx.JSON(200, map[string]string{
"uuid": fileChunk.UUID,
"uploaded": strconv.Itoa(fileChunk.IsUploaded),
"uploadID":fileChunk.UploadID,
"chunks": fileChunk.HasUploaded,
})

}

func NewMultipart(ctx *context.Context) { func NewMultipart(ctx *context.Context) {
if !setting.Attachment.Enabled { if !setting.Attachment.Enabled {
ctx.Error(404, "attachment is not enabled") ctx.Error(404, "attachment is not enabled")
@@ -291,15 +313,28 @@ func NewMultipart(ctx *context.Context) {


if setting.Attachment.StoreType == storage.MinioStorageType { if setting.Attachment.StoreType == storage.MinioStorageType {
uuid := gouuid.NewV4().String() uuid := gouuid.NewV4().String()
url, err := storage.NewMultiPartUpload(uuid)
uploadID, err := storage.NewMultiPartUpload(uuid)
if err != nil { if err != nil {
ctx.ServerError("NewMultipart", err) ctx.ServerError("NewMultipart", err)
return return
} }


_, err = models.InsertFileChunk(&models.FileChunk{
UUID: uuid,
UserID: ctx.User.ID,
UploadID: uploadID,
Md5: ctx.Params("md5"),
Size: ctx.ParamsInt64("size"),
})

if err != nil {
ctx.Error(500, fmt.Sprintf("InsertFileChunk: %v", err))
return
}

ctx.JSON(200, map[string]string{ ctx.JSON(200, map[string]string{
"uuid": uuid, "uuid": uuid,
"url": url,
"uploadID": uploadID,
}) })
} else { } else {
ctx.Error(404, "storage type is not enabled") ctx.Error(404, "storage type is not enabled")
@@ -307,18 +342,52 @@ func NewMultipart(ctx *context.Context) {
} }
} }


func GetMultipartUploadUrl(ctx *context.Context) {
uuid := ctx.Query("uuid")
uploadID := ctx.Params("uploadID")
partNumber := ctx.ParamsInt("partNumber")
size := ctx.ParamsInt64("size")

url,err := storage.GenMultiPartSignedUrl(uuid, uploadID, partNumber, size)
if err != nil {
ctx.Error(500, fmt.Sprintf("GenMultiPartSignedUrl failed: %v", err))
return
}

ctx.JSON(200, map[string]string{
"url": url,
})
}


func CompleteMultipart(ctx *context.Context) { func CompleteMultipart(ctx *context.Context) {
uuid := ctx.Query("uuid") uuid := ctx.Query("uuid")
uploadID := ctx.Query("uploadID") uploadID := ctx.Query("uploadID")
completedParts := ctx.Query("completedParts") completedParts := ctx.Query("completedParts")


_, err := storage.CompleteMultiPartUpload(uuid, uploadID, completedParts)
fileChunk, err := models.GetFileChunkByUUID(uuid)
if err != nil {
if models.IsErrFileChunkNotExist(err) {
ctx.Error(404)
} else {
ctx.ServerError("GetFileChunkByUUID", err)
}
return
}

_, err = storage.CompleteMultiPartUpload(uuid, uploadID, completedParts)
if err != nil { if err != nil {
ctx.Error(500, fmt.Sprintf("CompleteMultiPartUpload failed: %v", err)) ctx.Error(500, fmt.Sprintf("CompleteMultiPartUpload failed: %v", err))
return return
} }


fileChunk.IsUploaded = models.FileUploaded

err = models.UpdateFileChunk(fileChunk)
if err != nil {
ctx.Error(500, fmt.Sprintf("UpdateFileChunk: %v", err))
return
}

_, err = models.InsertAttachment(&models.Attachment{ _, err = models.InsertAttachment(&models.Attachment{
UUID: uuid, UUID: uuid,
UploaderID: ctx.User.ID, UploaderID: ctx.User.ID,
@@ -337,3 +406,30 @@ func CompleteMultipart(ctx *context.Context) {
"result_code": "0", "result_code": "0",
}) })
} }

func UpdateMultipart(ctx *context.Context) {
uuid := ctx.Query("uuid")
partNumber := ctx.QueryInt("partNumber")

fileChunk, err := models.GetFileChunkByUUID(uuid)
if err != nil {
if models.IsErrFileChunkNotExist(err) {
ctx.Error(404)
} else {
ctx.ServerError("GetFileChunkByUUID", err)
}
return
}

fileChunk.HasUploaded += "," + strconv.Itoa(partNumber)

err = models.UpdateFileChunk(fileChunk)
if err != nil {
ctx.Error(500, fmt.Sprintf("UpdateFileChunk: %v", err))
return
}

ctx.JSON(200, map[string]string{
"result_code": "0",
})
}

+ 3
- 0
routers/routes/routes.go View File

@@ -521,8 +521,11 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Get("/get_pre_url", repo.GetPresignedPutObjectURL) m.Get("/get_pre_url", repo.GetPresignedPutObjectURL)
m.Post("/add", repo.AddAttachment) m.Post("/add", repo.AddAttachment)
m.Post("/private", repo.UpdatePublicAttachment) m.Post("/private", repo.UpdatePublicAttachment)
m.Get("/get_chunks", repo.GetSuccessChunks)
m.Get("/new_multipart", repo.NewMultipart) m.Get("/new_multipart", repo.NewMultipart)
m.Get("/get_multipart_url", repo.GetMultipartUploadUrl)
m.Post("/complete_multipart", repo.CompleteMultipart) m.Post("/complete_multipart", repo.CompleteMultipart)
m.Post("/update_multipart", repo.UpdateMultipart)
}, reqSignIn) }, reqSignIn)


m.Group("/:username", func() { m.Group("/:username", func() {


Loading…
Cancel
Save