Browse Source

Merge branch 'modelarts' of git.pcl.ac.cn:yoyoyard/opendata into modelarts

master
Gitea 4 years ago
parent
commit
8bb3dc4e82
12 changed files with 460 additions and 58 deletions
  1. +11
    -0
      custom/conf/app.ini.sample
  2. +5
    -4
      models/attachment.go
  3. +12
    -6
      models/file_chunk.go
  4. +52
    -0
      modules/obs/temporary.go
  5. +17
    -0
      modules/setting/setting.go
  6. +153
    -0
      modules/storage/obs.go
  7. +9
    -0
      modules/storage/storage.go
  8. +154
    -28
      routers/repo/attachment.go
  9. +1
    -0
      routers/routes/routes.go
  10. +3
    -3
      templates/repo/home.tmpl
  11. +2
    -0
      templates/repo/sub_menu.tmpl
  12. +41
    -17
      web_src/js/components/MinioUploader.vue

+ 11
- 0
custom/conf/app.ini.sample View File

@@ -1069,3 +1069,14 @@ PASSWORD = 4BPmgvK2hb2Eywwyp4YZRY4B7yQf4DAC
[blockchain] [blockchain]
HOST = http://192.168.207.84:3002/ HOST = http://192.168.207.84:3002/
COMMIT_VALID_DATE = 2021-01-15 COMMIT_VALID_DATE = 2021-01-15

[obs]
ENDPOINT = 112.95.163.82
ACCESS_KEY_ID = FDP3LRMHLB9S77VWEHE3
SECRET_ACCESS_KEY = LyM82Wk80pgjhs2z7AdDcsdpCWhbsJtSzQ7hkESN
BUCKET = testopendata
LOCATION = cn-south-222
BASE_PATH = attachment/

[modelarts]
ENDPOINT = 112.95.163.80

+ 5
- 4
models/attachment.go View File

@@ -41,6 +41,7 @@ type Attachment struct {
Size int64 `xorm:"DEFAULT 0"` Size int64 `xorm:"DEFAULT 0"`
IsPrivate bool `xorm:"DEFAULT false"` IsPrivate bool `xorm:"DEFAULT false"`
DecompressState int32 `xorm:"DEFAULT 0"` DecompressState int32 `xorm:"DEFAULT 0"`
Type int `xorm:"DEFAULT 0"`
CreatedUnix timeutil.TimeStamp `xorm:"created"` CreatedUnix timeutil.TimeStamp `xorm:"created"`
} }


@@ -350,7 +351,7 @@ func GetUnDecompressAttachments() ([]*Attachment, error) {


func getUnDecompressAttachments(e Engine) ([]*Attachment, error) { func getUnDecompressAttachments(e Engine) ([]*Attachment, error) {
attachments := make([]*Attachment, 0, 10) attachments := make([]*Attachment, 0, 10)
return attachments, e.Where("decompress_state = ? and dataset_id != 0 and name like '%.zip'", DecompressStateInit).Find(&attachments)
return attachments, e.Where("decompress_state = ? and dataset_id != 0 and type = ? and name like '%.zip'", DecompressStateInit, TypeCloudBrainOne).Find(&attachments)
} }


func GetAllPublicAttachments() ([]*AttachmentUsername, error) { func GetAllPublicAttachments() ([]*AttachmentUsername, error) {
@@ -360,7 +361,7 @@ func GetAllPublicAttachments() ([]*AttachmentUsername, error) {
func getAllPublicAttachments(e Engine) ([]*AttachmentUsername, error) { func getAllPublicAttachments(e Engine) ([]*AttachmentUsername, error) {
attachments := make([]*AttachmentUsername, 0, 10) attachments := make([]*AttachmentUsername, 0, 10)
if err := e.Table("attachment").Join("LEFT", "`user`", "attachment.uploader_id "+ if err := e.Table("attachment").Join("LEFT", "`user`", "attachment.uploader_id "+
"= `user`.id").Where("decompress_state= ? and is_private= ?", DecompressStateDone, false).Find(&attachments); err != nil {
"= `user`.id").Where("decompress_state= ? and is_private= ? and type = ?", DecompressStateDone, false, TypeCloudBrainOne).Find(&attachments); err != nil {
return nil, err return nil, err
} }
return attachments, nil return attachments, nil
@@ -378,7 +379,7 @@ func GetPrivateAttachments(username string) ([]*AttachmentUsername, error) {
func getPrivateAttachments(e Engine, userID int64) ([]*AttachmentUsername, error) { func getPrivateAttachments(e Engine, userID int64) ([]*AttachmentUsername, error) {
attachments := make([]*AttachmentUsername, 0, 10) attachments := make([]*AttachmentUsername, 0, 10)
if err := e.Table("attachment").Join("LEFT", "`user`", "attachment.uploader_id "+ if err := e.Table("attachment").Join("LEFT", "`user`", "attachment.uploader_id "+
"= `user`.id").Where("decompress_state= ? and uploader_id= ?", DecompressStateDone, userID).Find(&attachments); err != nil {
"= `user`.id").Where("decompress_state= ? and uploader_id= ? and type = ?", DecompressStateDone, userID, TypeCloudBrainOne).Find(&attachments); err != nil {
return nil, err return nil, err
} }
return attachments, nil return attachments, nil
@@ -406,7 +407,7 @@ func GetAllUserAttachments(userID int64) ([]*AttachmentUsername, error) {
func getAllUserAttachments(e Engine, userID int64) ([]*AttachmentUsername, error) { func getAllUserAttachments(e Engine, userID int64) ([]*AttachmentUsername, error) {
attachments := make([]*AttachmentUsername, 0, 10) attachments := make([]*AttachmentUsername, 0, 10)
if err := e.Table("attachment").Join("LEFT", "`user`", "attachment.uploader_id "+ if err := e.Table("attachment").Join("LEFT", "`user`", "attachment.uploader_id "+
"= `user`.id").Where("decompress_state= ? and (uploader_id= ? or is_private = ?)", DecompressStateDone, userID, false).Find(&attachments); err != nil {
"= `user`.id").Where("decompress_state= ? and type = ? and (uploader_id= ? or is_private = ?)", DecompressStateDone, TypeCloudBrainOne, userID, false).Find(&attachments); err != nil {
return nil, err return nil, err
} }
return attachments, nil return attachments, nil


+ 12
- 6
models/file_chunk.go View File

@@ -10,6 +10,11 @@ const (
FileUploaded FileUploaded
) )


const (
TypeCloudBrainOne = 0
TypeCloudBrainTwo = 1
)

type FileChunk struct { type FileChunk struct {
ID int64 `xorm:"pk autoincr"` ID int64 `xorm:"pk autoincr"`
UUID string `xorm:"uuid UNIQUE"` UUID string `xorm:"uuid UNIQUE"`
@@ -19,7 +24,8 @@ type FileChunk struct {
TotalChunks int TotalChunks int
Size int64 Size int64
UserID int64 `xorm:"INDEX"` UserID int64 `xorm:"INDEX"`
CompletedParts []string `xorm:"DEFAULT """` // chunkNumber+etag eg: ,1-asqwewqe21312312.2-123hjkas
Type int `xorm:"INDEX DEFAULT 0"`
CompletedParts []string `xorm:"DEFAULT ''"` // chunkNumber+etag eg: ,1-asqwewqe21312312.2-123hjkas
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"` CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"` UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
} }
@@ -41,14 +47,14 @@ func getFileChunkByMD5(e Engine, md5 string) (*FileChunk, error) {
} }


// GetFileChunkByMD5 returns fileChunk by given id // GetFileChunkByMD5 returns fileChunk by given id
func GetFileChunkByMD5AndUser(md5 string, userID int64) (*FileChunk, error) {
return getFileChunkByMD5AndUser(x, md5, userID)
func GetFileChunkByMD5AndUser(md5 string, userID int64, typeCloudBrain int) (*FileChunk, error) {
return getFileChunkByMD5AndUser(x, md5, userID, typeCloudBrain)
} }


func getFileChunkByMD5AndUser(e Engine, md5 string, userID int64) (*FileChunk, error) {
func getFileChunkByMD5AndUser(e Engine, md5 string, userID int64, typeCloudBrain int) (*FileChunk, error) {
fileChunk := new(FileChunk) fileChunk := new(FileChunk)


if has, err := e.Where("md5 = ? and user_id = ?", md5, userID).Get(fileChunk); err != nil {
if has, err := e.Where("md5 = ? and user_id = ? and type = ?", md5, userID, typeCloudBrain).Get(fileChunk); err != nil {
return nil, err return nil, err
} else if !has { } else if !has {
return nil, ErrFileChunkNotExist{md5, ""} return nil, ErrFileChunkNotExist{md5, ""}
@@ -89,6 +95,6 @@ func UpdateFileChunk(fileChunk *FileChunk) error {
func updateFileChunk(e Engine, fileChunk *FileChunk) error { func updateFileChunk(e Engine, fileChunk *FileChunk) error {
var sess *xorm.Session var sess *xorm.Session
sess = e.Where("uuid = ?", fileChunk.UUID) sess = e.Where("uuid = ?", fileChunk.UUID)
_, err := sess.Cols("is_uploaded", "completed_parts").Update(fileChunk)
_, err := sess.Cols("is_uploaded").Update(fileChunk)
return err return err
} }

+ 52
- 0
modules/obs/temporary.go View File

@@ -16,6 +16,7 @@ package obs
import ( import (
"errors" "errors"
"fmt" "fmt"
"github.com/unknwon/com"
"io" "io"
"net/http" "net/http"
"os" "os"
@@ -788,3 +789,54 @@ func (obsClient ObsClient) GetBucketRequestPaymentWithSignedUrl(signedUrl string
} }
return return
} }


func (obsClient ObsClient) CreateUploadPartSignedUrl(bucketName, objectKey, uploadId string, partNumber int, partSize int64) (string, error) {
requestURL := ""

input := &UploadPartInput{}
input.Bucket = bucketName
input.Key = objectKey
input.PartNumber = partNumber
input.UploadId = uploadId
//input.ContentMD5 = _input.ContentMD5
//input.SourceFile = _input.SourceFile
//input.Offset = _input.Offset
input.PartSize = partSize
//input.SseHeader = _input.SseHeader
//input.Body = _input.Body

params, headers, _, err := input.trans(obsClient.conf.signature == SignatureObs)
if err != nil {
return requestURL, err
}

if params == nil {
params = make(map[string]string)
}

if headers == nil {
headers = make(map[string][]string)
}

var extensions []extensionOptions
for _, extension := range extensions {
if extensionHeader, ok := extension.(extensionHeaders); ok {
_err := extensionHeader(headers, obsClient.conf.signature == SignatureObs)
if _err != nil {
doLog(LEVEL_WARN, fmt.Sprintf("set header with error: %v", _err))
}
} else {
doLog(LEVEL_WARN, "Unsupported extensionOptions")
}
}

headers["Content-Length"] = []string{com.ToStr(partNumber,10)}

requestURL, err = obsClient.doAuth(HTTP_PUT, bucketName, objectKey, params, headers, "")
if err != nil {
return requestURL, nil
}

return requestURL, nil
}

+ 17
- 0
modules/setting/setting.go View File

@@ -447,6 +447,15 @@ var (
//blockchain config //blockchain config
BlockChainHost string BlockChainHost string
CommitValidDate string CommitValidDate string

//obs config
Endpoint string
AccessKeyID string
SecretAccessKey string
Bucket string
Location string
BasePath string
//RealPath string
) )


// DateLang transforms standard language locale name to corresponding value in datetime plugin. // DateLang transforms standard language locale name to corresponding value in datetime plugin.
@@ -1131,6 +1140,14 @@ func NewContext() {
sec = Cfg.Section("blockchain") sec = Cfg.Section("blockchain")
BlockChainHost = sec.Key("HOST").MustString("http://192.168.136.66:3302/") BlockChainHost = sec.Key("HOST").MustString("http://192.168.136.66:3302/")
CommitValidDate = sec.Key("COMMIT_VALID_DATE").MustString("2021-01-15") CommitValidDate = sec.Key("COMMIT_VALID_DATE").MustString("2021-01-15")

sec = Cfg.Section("obs")
Endpoint = sec.Key("ENDPOINT").MustString("112.95.163.82")
AccessKeyID = sec.Key("ACCESS_KEY_ID").MustString("")
SecretAccessKey = sec.Key("SECRET_ACCESS_KEY").MustString("")
Bucket = sec.Key("BUCKET").MustString("testopendata")
Location = sec.Key("LOCATION").MustString("cn-south-222")
BasePath = sec.Key("BASE_PATH").MustString("attachment/")
} }


func loadInternalToken(sec *ini.Section) string { func loadInternalToken(sec *ini.Section) string {


+ 153
- 0
modules/storage/obs.go View File

@@ -0,0 +1,153 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package storage

import (
"io"
"path"
"strconv"
"strings"

"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/obs"
"code.gitea.io/gitea/modules/setting"
)

//check if has the object
func ObsHasObject(path string) (bool, error) {
hasObject := false
output, err := ObsCli.ListObjects(&obs.ListObjectsInput{Bucket:setting.Bucket})
if err != nil {
log.Error("ListObjects failed:%v", err)
return hasObject, err
}

for _, obj := range output.Contents {
if path == obj.Key {
hasObject = true
break
}
}

return hasObject, nil
}

func GetObsPartInfos(uuid string, uploadID string) (string, error) {
key := strings.TrimPrefix(path.Join(setting.BasePath, path.Join(uuid[0:1], uuid[1:2], uuid)), "/")

output, err := ObsCli.ListParts(&obs.ListPartsInput{
Bucket: setting.Bucket,
Key: key,
UploadId: uploadID,
})
if err != nil {
log.Error("ListParts failed:", err.Error())
return "", err
}

var chunks string
for _, partInfo := range output.Parts {
chunks += strconv.Itoa(partInfo.PartNumber) + "-" + partInfo.ETag + ","
}

return chunks, nil
}

func NewObsMultiPartUpload(uuid string) (string, error) {
input := &obs.InitiateMultipartUploadInput{}
input.Bucket = setting.Bucket
input.Key = strings.TrimPrefix(path.Join(setting.BasePath, path.Join(uuid[0:1], uuid[1:2], uuid)), "/")

output, err := ObsCli.InitiateMultipartUpload(input)
if err != nil {
log.Error("InitiateMultipartUpload failed:", err.Error())
return "", err
}

return output.UploadId, nil
}

func CompleteObsMultiPartUpload(uuid string, uploadID string) error {
input := &obs.CompleteMultipartUploadInput{}
input.Bucket = setting.Bucket
input.Key = strings.TrimPrefix(path.Join(setting.BasePath, path.Join(uuid[0:1], uuid[1:2], uuid)), "/")
input.UploadId = uploadID
output, err := ObsCli.ListParts(&obs.ListPartsInput{
Bucket: setting.Bucket,
Key: input.Key,
UploadId: uploadID,
})
if err != nil {
log.Error("ListParts failed:", err.Error())
return err
}

for _, partInfo := range output.Parts {
input.Parts = append(input.Parts, obs.Part{
PartNumber: partInfo.PartNumber,
ETag: partInfo.ETag,
})
}

_, err = ObsCli.CompleteMultipartUpload(input)
if err != nil {
log.Error("CompleteMultipartUpload failed:", err.Error())
return err
}

return nil
}

func ObsUploadPart(uuid string, uploadId string, partNumber int, partSize int64, body io.Reader) (string, error) {
input := &obs.UploadPartInput{}
input.PartNumber = partNumber
input.Key = strings.TrimPrefix(path.Join(setting.BasePath, path.Join(uuid[0:1], uuid[1:2], uuid)), "/")
input.UploadId = uploadId
input.Bucket = setting.Bucket
input.PartSize = partSize
input.Body = body
output, err := ObsCli.UploadPart(input)
if err != nil {
log.Error("UploadPart failed:", err.Error())
return "", err
}

return output.ETag, nil
}

func ObsGenMultiPartSignedUrl(uuid string, uploadId string, partNumber int, partSize int64) (string, error) {
/*
input := &obs.CreateSignedUrlInput{}
input.Bucket = setting.Bucket
input.Key = strings.TrimPrefix(path.Join(setting.BasePath, path.Join(uuid[0:1], uuid[1:2], uuid)), "/")
input.Expires = int(PresignedUploadPartUrlExpireTime)
input.Method = obs.HTTP_PUT

input.QueryParams = map[string]string{
"Bucket": input.Bucket,
"Key": input.Key,
"PartNumber": com.ToStr(partNumber,10),
"UploadId": uploadId,
"PartSize": com.ToStr(partSize,10),
}

input.Headers = map[string]string{

}

*/

Key := strings.TrimPrefix(path.Join(setting.BasePath, path.Join(uuid[0:1], uuid[1:2], uuid)), "/")
url, err := ObsCli.CreateUploadPartSignedUrl(setting.Bucket, Key, uploadId, partNumber, partSize)
if err != nil {
log.Error("CreateSignedUrl failed:", err.Error())
return "", err
}

log.Info(url)

return url, nil

}

+ 9
- 0
modules/storage/storage.go View File

@@ -8,6 +8,8 @@ import (
"fmt" "fmt"
"io" "io"


"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/obs"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
) )


@@ -40,6 +42,7 @@ func Copy(dstStorage ObjectStorage, dstPath string, srcStorage ObjectStorage, sr
var ( var (
// Attachments represents attachments storage // Attachments represents attachments storage
Attachments ObjectStorage Attachments ObjectStorage
ObsCli *obs.ObsClient
) )


// Init init the stoarge // Init init the stoarge
@@ -63,6 +66,12 @@ func Init() error {
return fmt.Errorf("Unsupported attachment store type: %s", setting.Attachment.StoreType) return fmt.Errorf("Unsupported attachment store type: %s", setting.Attachment.StoreType)
} }


ObsCli, err = obs.New(setting.AccessKeyID, setting.SecretAccessKey, setting.Endpoint)
if err != nil {
log.Error("obs.New failed:", err)
return err
}

if err != nil { if err != nil {
return err return err
} }


+ 154
- 28
routers/repo/attachment.go View File

@@ -5,6 +5,15 @@
package repo package repo


import ( import (
contexExt "context"
"encoding/json"
"errors"
"fmt"
"mime/multipart"
"net/http"
"strconv"
"strings"

"code.gitea.io/gitea/models" "code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
@@ -13,12 +22,6 @@ import (
"code.gitea.io/gitea/modules/storage" "code.gitea.io/gitea/modules/storage"
"code.gitea.io/gitea/modules/upload" "code.gitea.io/gitea/modules/upload"
"code.gitea.io/gitea/modules/worker" "code.gitea.io/gitea/modules/worker"
contexExt "context"
"encoding/json"
"fmt"
"net/http"
"strconv"
"strings"


gouuid "github.com/satori/go.uuid" gouuid "github.com/satori/go.uuid"
) )
@@ -37,6 +40,15 @@ type CloudBrainDataset struct {
CreateTime string `json:"created_at"` CreateTime string `json:"created_at"`
} }


type UploadForm struct {
UploadID string `form:"uploadId"`
UuID string `form:"uuid"`
PartSize int64 `form:"size"`
Offset int64 `form:"offset"`
PartNumber int `form:"chunkNumber"`
PartFile multipart.File `form:"file"`
}

func RenderAttachmentSettings(ctx *context.Context) { func RenderAttachmentSettings(ctx *context.Context) {
renderAttachmentSettings(ctx) renderAttachmentSettings(ctx)
} }
@@ -340,9 +352,16 @@ func UpdateAttachmentDecompressState(ctx *context.Context) {


func GetSuccessChunks(ctx *context.Context) { func GetSuccessChunks(ctx *context.Context) {
fileMD5 := ctx.Query("md5") fileMD5 := ctx.Query("md5")
typeCloudBrain := ctx.QueryInt("type")
var chunks string var chunks string


fileChunk, err := models.GetFileChunkByMD5AndUser(fileMD5, ctx.User.ID)
err := checkTypeCloudBrain(typeCloudBrain)
if err != nil {
ctx.ServerError("checkTypeCloudBrain failed", err)
return
}

fileChunk, err := models.GetFileChunkByMD5AndUser(fileMD5, ctx.User.ID, typeCloudBrain)
if err != nil { if err != nil {
if models.IsErrFileChunkNotExist(err) { if models.IsErrFileChunkNotExist(err) {
ctx.JSON(200, map[string]string{ ctx.JSON(200, map[string]string{
@@ -357,12 +376,22 @@ func GetSuccessChunks(ctx *context.Context) {
return return
} }


isExist, err := storage.Attachments.HasObject(models.AttachmentRelativePath(fileChunk.UUID))
if err != nil {
ctx.ServerError("HasObject failed", err)
return
isExist := false
if typeCloudBrain == models.TypeCloudBrainOne {
isExist, err = storage.Attachments.HasObject(models.AttachmentRelativePath(fileChunk.UUID))
if err != nil {
ctx.ServerError("HasObject failed", err)
return
}
} else {
isExist, err = storage.ObsHasObject(models.AttachmentRelativePath(fileChunk.UUID))
if err != nil {
ctx.ServerError("ObsHasObject failed", err)
return
}
} }



if isExist { if isExist {
if fileChunk.IsUploaded == models.FileNotUploaded { if fileChunk.IsUploaded == models.FileNotUploaded {
log.Info("the file has been uploaded but not recorded") log.Info("the file has been uploaded but not recorded")
@@ -380,10 +409,18 @@ func GetSuccessChunks(ctx *context.Context) {
} }
} }


chunks, err = storage.GetPartInfos(fileChunk.UUID, fileChunk.UploadID)
if err != nil {
ctx.ServerError("GetPartInfos failed", err)
return
if typeCloudBrain == models.TypeCloudBrainOne {
chunks, err = storage.GetPartInfos(fileChunk.UUID, fileChunk.UploadID)
if err != nil {
ctx.ServerError("GetPartInfos failed", err)
return
}
} else {
chunks, err = storage.GetObsPartInfos(fileChunk.UUID, fileChunk.UploadID)
if err != nil {
ctx.ServerError("GetObsPartInfos failed", err)
return
}
} }
} }


@@ -445,6 +482,13 @@ func NewMultipart(ctx *context.Context) {
return return
} }


typeCloudBrain := ctx.QueryInt("type")
err = checkTypeCloudBrain(typeCloudBrain)
if err != nil {
ctx.ServerError("checkTypeCloudBrain failed", err)
return
}

if setting.Attachment.StoreType == storage.MinioStorageType { if setting.Attachment.StoreType == storage.MinioStorageType {
totalChunkCounts := ctx.QueryInt("totalChunkCounts") totalChunkCounts := ctx.QueryInt("totalChunkCounts")
if totalChunkCounts > minio_ext.MaxPartsCount { if totalChunkCounts > minio_ext.MaxPartsCount {
@@ -459,10 +503,19 @@ func NewMultipart(ctx *context.Context) {
} }


uuid := gouuid.NewV4().String() uuid := gouuid.NewV4().String()
uploadID, err := storage.NewMultiPartUpload(uuid)
if err != nil {
ctx.ServerError("NewMultipart", err)
return
var uploadID string
if typeCloudBrain == models.TypeCloudBrainOne {
uploadID, err = storage.NewMultiPartUpload(uuid)
if err != nil {
ctx.ServerError("NewMultipart", err)
return
}
} else {
uploadID, err = storage.NewObsMultiPartUpload(uuid)
if err != nil {
ctx.ServerError("NewObsMultiPartUpload", err)
return
}
} }


_, err = models.InsertFileChunk(&models.FileChunk{ _, err = models.InsertFileChunk(&models.FileChunk{
@@ -472,6 +525,7 @@ func NewMultipart(ctx *context.Context) {
Md5: ctx.Query("md5"), Md5: ctx.Query("md5"),
Size: fileSize, Size: fileSize,
TotalChunks: totalChunkCounts, TotalChunks: totalChunkCounts,
Type: typeCloudBrain,
}) })


if err != nil { if err != nil {
@@ -495,25 +549,80 @@ func GetMultipartUploadUrl(ctx *context.Context) {
partNumber := ctx.QueryInt("chunkNumber") partNumber := ctx.QueryInt("chunkNumber")
size := ctx.QueryInt64("size") size := ctx.QueryInt64("size")


if size > minio_ext.MinPartSize {
ctx.Error(400, fmt.Sprintf("chunk size(%d) is too big", size))
typeCloudBrain := ctx.QueryInt("type")
err := checkTypeCloudBrain(typeCloudBrain)
if err != nil {
ctx.ServerError("checkTypeCloudBrain failed", err)
return return
} }


url, err := storage.GenMultiPartSignedUrl(uuid, uploadID, partNumber, size)
url := ""
if typeCloudBrain == models.TypeCloudBrainOne {
if size > minio_ext.MinPartSize {
ctx.Error(400, fmt.Sprintf("chunk size(%d) is too big", size))
return
}

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

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

func UploadPart(ctx *context.Context) {
tmp, err := ctx.Req.Body().String()
log.Info(tmp)

err = ctx.Req.ParseMultipartForm(100*1024*1024)
if err != nil { if err != nil {
ctx.Error(500, fmt.Sprintf("GenMultiPartSignedUrl failed: %v", err))
ctx.Error(http.StatusBadRequest, fmt.Sprintf("ParseMultipartForm failed: %v", err))
return
}

file, fileHeader, err := ctx.Req.FormFile("file")
log.Info(ctx.Req.Form.Get("file"))
if err != nil {
ctx.Error(http.StatusBadRequest, fmt.Sprintf("FormFile failed: %v", err))
return
}



log.Info(fileHeader.Filename)

etag, err := storage.ObsUploadPart("", "", 1, 1, file)
if err != nil {
ctx.Error(500, fmt.Sprintf("ObsUploadPart failed: %v", err))
return return
} }


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


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")
typeCloudBrain := ctx.QueryInt("type")

err := checkTypeCloudBrain(typeCloudBrain)
if err != nil {
ctx.ServerError("checkTypeCloudBrain failed", err)
return
}


fileChunk, err := models.GetFileChunkByUUID(uuid) fileChunk, err := models.GetFileChunkByUUID(uuid)
if err != nil { if err != nil {
@@ -525,10 +634,18 @@ func CompleteMultipart(ctx *context.Context) {
return return
} }


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


fileChunk.IsUploaded = models.FileUploaded fileChunk.IsUploaded = models.FileUploaded
@@ -546,6 +663,7 @@ func CompleteMultipart(ctx *context.Context) {
Name: ctx.Query("file_name"), Name: ctx.Query("file_name"),
Size: ctx.QueryInt64("size"), Size: ctx.QueryInt64("size"),
DatasetID: ctx.QueryInt64("dataset_id"), DatasetID: ctx.QueryInt64("dataset_id"),
Type: typeCloudBrain,
}) })


if err != nil { if err != nil {
@@ -704,3 +822,11 @@ func queryDatasets(ctx *context.Context, attachs []*models.AttachmentUsername) {
}) })
return return
} }

func checkTypeCloudBrain(typeCloudBrain int) error {
if typeCloudBrain != models.TypeCloudBrainOne && typeCloudBrain != models.TypeCloudBrainTwo {
log.Error("type error:", typeCloudBrain)
return errors.New("type error")
}
return nil
}

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

@@ -529,6 +529,7 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Get("/get_multipart_url", repo.GetMultipartUploadUrl) m.Get("/get_multipart_url", repo.GetMultipartUploadUrl)
m.Post("/complete_multipart", repo.CompleteMultipart) m.Post("/complete_multipart", repo.CompleteMultipart)
m.Post("/update_chunk", repo.UpdateMultipart) m.Post("/update_chunk", repo.UpdateMultipart)
m.Post("/upload_part", repo.UploadPart)
}, reqSignIn) }, reqSignIn)


m.Group("/attachments", func() { m.Group("/attachments", func() {


+ 3
- 3
templates/repo/home.tmpl View File

@@ -3,13 +3,13 @@
{{template "repo/header" .}} {{template "repo/header" .}}
<div class="ui container"> <div class="ui container">
{{template "base/alert" .}} {{template "base/alert" .}}
<div class="ui repo-description">
<div id="repo-desc">
<div class="ui repo-description stackable grid">
<div id="repo-desc" class="ui twelve wide column">
{{if .Repository.DescriptionHTML}}<span class="description">{{.Repository.DescriptionHTML}}</span>{{else if .IsRepositoryAdmin}}<span class="no-description text-italic">{{.i18n.Tr "repo.no_desc"}}</span>{{end}} {{if .Repository.DescriptionHTML}}<span class="description">{{.Repository.DescriptionHTML}}</span>{{else if .IsRepositoryAdmin}}<span class="no-description text-italic">{{.i18n.Tr "repo.no_desc"}}</span>{{end}}
<a class="link" href="{{.Repository.Website}}">{{.Repository.Website}}</a> <a class="link" href="{{.Repository.Website}}">{{.Repository.Website}}</a>
</div> </div>
{{if .RepoSearchEnabled}} {{if .RepoSearchEnabled}}
<div class="ui repo-search">
<div class="ui repo-search four wide column">
<form class="ui form ignore-dirty" action="{{.RepoLink}}/search" method="get"> <form class="ui form ignore-dirty" action="{{.RepoLink}}/search" method="get">
<div class="field"> <div class="field">
<div class="ui action input"> <div class="ui action input">


+ 2
- 0
templates/repo/sub_menu.tmpl View File

@@ -13,9 +13,11 @@
<div class="item"> <div class="item">
<a class="ui" href="#">{{svg "octicon-database" 16}} <b>{{SizeFmt .Repository.Size}}</b></a> <a class="ui" href="#">{{svg "octicon-database" 16}} <b>{{SizeFmt .Repository.Size}}</b></a>
</div> </div>
{{if (not .IsPrivate)}}
<div class="item"> <div class="item">
<a class="ui" href="#">{{svg "octicon-desktop-download" 16}} <b>{{.Repository.CloneCnt}} {{.i18n.Tr "repo.clone_cnt" }}</b></a> <a class="ui" href="#">{{svg "octicon-desktop-download" 16}} <b>{{.Repository.CloneCnt}} {{.i18n.Tr "repo.clone_cnt" }}</b></a>
</div> </div>
{{end}}
{{end}} {{end}}
</div> </div>
</div> </div>


+ 41
- 17
web_src/js/components/MinioUploader.vue View File

@@ -21,6 +21,7 @@ import qs from 'qs';
import createDropzone from '../features/dropzone.js'; import createDropzone from '../features/dropzone.js';


const {_AppSubUrl, _StaticUrlPrefix, csrf} = window.config; const {_AppSubUrl, _StaticUrlPrefix, csrf} = window.config;
const cloud_brain_type = 1;


export default { export default {
data() { data() {
@@ -128,9 +129,9 @@ export default {


finishUpload(file) { finishUpload(file) {
this.emitDropzoneSuccess(file); this.emitDropzoneSuccess(file);
setTimeout(() => {
window.location.reload();
}, 1000);
// setTimeout(() => {
// window.location.reload();
// }, 1000);
}, },


computeMD5(file) { computeMD5(file) {
@@ -255,6 +256,7 @@ export default {
const params = { const params = {
params: { params: {
md5: file.uniqueIdentifier, md5: file.uniqueIdentifier,
type: cloud_brain_type,
_csrf: csrf _csrf: csrf
} }
}; };
@@ -283,6 +285,7 @@ export default {
md5: file.uniqueIdentifier, md5: file.uniqueIdentifier,
size: file.size, size: file.size,
fileType: file.type, fileType: file.type,
type: cloud_brain_type,
_csrf: csrf _csrf: csrf
} }
}); });
@@ -323,6 +326,7 @@ export default {
uploadID: file.uploadID, uploadID: file.uploadID,
size: partSize, size: partSize,
chunkNumber: currentChunk + 1, chunkNumber: currentChunk + 1,
type: cloud_brain_type,
_csrf: csrf _csrf: csrf
} }
}); });
@@ -345,30 +349,49 @@ export default {
}) })
); );
} }

async function uploadPart(currentChunk, partSize, e) {
console.log(e);
let params = new FormData();
params.append("uuid", file.uuid);
params.append("uploadId", file.uploadID);
params.append("size", partSize);
params.append("chunkNumber", currentChunk + 1);
params.append("file", e.target.file);
params.append("_csrf", csrf);
return await axios.post('/attachments/upload_part',
params,
{headers: {'Content-Type': 'multipart/form-data'}}
);
}

async function uploadChunk(e) { async function uploadChunk(e) {
try { try {
if (!checkSuccessChunks()) { if (!checkSuccessChunks()) {
const start = currentChunk * chunkSize; const start = currentChunk * chunkSize;
const partSize = const partSize =
start + chunkSize >= file.size ? file.size - start : chunkSize; start + chunkSize >= file.size ? file.size - start : chunkSize;
await uploadPart(currentChunk, partSize, e);

// 获取分片上传url // 获取分片上传url
await getUploadChunkUrl(currentChunk, partSize);
if (urls[currentChunk] != '') {
// 上传到minio
await uploadMinio(urls[currentChunk], e);
if (etags[currentChunk] != '') {
// 更新数据库:分片上传结果
//await updateChunk(currentChunk);
} else {
console.log("上传到minio uploadChunk etags[currentChunk] == ''");// TODO
}
} else {
console.log("uploadChunk urls[currentChunk] != ''");// TODO
}
// await getUploadChunkUrl(currentChunk, partSize);
// if (urls[currentChunk] != '') {
// // 上传到minio
// await uploadMinio(urls[currentChunk], e);
// if (etags[currentChunk] != '') {
// // 更新数据库:分片上传结果
// //await updateChunk(currentChunk);
// } else {
// console.log("上传到minio uploadChunk etags[currentChunk] == ''");// TODO
// }
// } else {
// console.log("uploadChunk urls[currentChunk] != ''");// TODO
// }

} }
} catch (error) { } catch (error) {
this.emitDropzoneFailed(file);
console.log(error); console.log(error);
this.emitDropzoneFailed(file);
} }
} }


@@ -381,6 +404,7 @@ export default {
file_name: file.name, file_name: file.name,
size: file.size, size: file.size,
dataset_id: file.datasetId, dataset_id: file.datasetId,
type: cloud_brain_type,
_csrf: csrf _csrf: csrf
}) })
); );


Loading…
Cancel
Save