mirror of
https://github.com/haiwen/seafile-server.git
synced 2025-07-16 08:15:51 +00:00
* Retry upload when detecting concurrent uploads * Check conflict when update branch and set concurrent upload error Co-authored-by: 杨赫然 <heran.yang@seafile.com>
174 lines
4.3 KiB
Go
174 lines
4.3 KiB
Go
package main
|
|
|
|
import "fmt"
|
|
import "io"
|
|
import "sync"
|
|
import "flag"
|
|
import "log"
|
|
import "encoding/json"
|
|
import "bytes"
|
|
import "net/http"
|
|
import "mime/multipart"
|
|
import "path/filepath"
|
|
|
|
import "gopkg.in/ini.v1"
|
|
import "github.com/haiwen/seafile-server/fileserver/searpc"
|
|
|
|
type Options struct {
|
|
server string
|
|
username string
|
|
password string
|
|
repoID string
|
|
threadNum int
|
|
}
|
|
|
|
var confPath string
|
|
var rpcPipePath string
|
|
var options Options
|
|
var rpcclient *searpc.Client
|
|
|
|
func init() {
|
|
flag.StringVar(&confPath, "c", "", "config file path")
|
|
flag.StringVar(&rpcPipePath, "p", "", "rpc pipe path")
|
|
}
|
|
|
|
func main() {
|
|
flag.Parse()
|
|
|
|
pipePath := filepath.Join(rpcPipePath, "seafile.sock")
|
|
rpcclient = searpc.Init(pipePath, "seafserv-threaded-rpcserver")
|
|
|
|
config, err := ini.Load(confPath)
|
|
if err != nil {
|
|
log.Fatalf("Failed to load config file: %v", err)
|
|
}
|
|
section, err := config.GetSection("account")
|
|
if err != nil {
|
|
log.Fatal("No account section in config file.")
|
|
}
|
|
|
|
key, err := section.GetKey("server")
|
|
if err == nil {
|
|
options.server = key.String()
|
|
}
|
|
|
|
key, err = section.GetKey("username")
|
|
if err == nil {
|
|
options.username = key.String()
|
|
}
|
|
|
|
key, err = section.GetKey("password")
|
|
if err == nil {
|
|
options.password = key.String()
|
|
}
|
|
key, err = section.GetKey("repoid")
|
|
if err == nil {
|
|
options.repoID = key.String()
|
|
}
|
|
key, err = section.GetKey("thread_num")
|
|
if err == nil {
|
|
options.threadNum, _ = key.Int()
|
|
}
|
|
|
|
objID := "{\"parent_dir\":\"/\"}"
|
|
token, err := rpcclient.Call("seafile_web_get_access_token", options.repoID, objID, "upload", options.username, false)
|
|
if err != nil {
|
|
log.Fatal("Failed to get web access token\n")
|
|
}
|
|
accessToken, _ := token.(string)
|
|
|
|
url := fmt.Sprintf("%s:8082/upload-api/%s", options.server, accessToken)
|
|
content := []byte("123456")
|
|
|
|
var group sync.WaitGroup
|
|
for i := 0; i < options.threadNum; i++ {
|
|
group.Add(1)
|
|
go func(i int) {
|
|
values := make(map[string]io.Reader)
|
|
values["file"] = bytes.NewReader(content)
|
|
values["parent_dir"] = bytes.NewBuffer([]byte("/"))
|
|
// values["relative_path"] = bytes.NewBuffer([]byte(relativePath))
|
|
values["replace"] = bytes.NewBuffer([]byte("0"))
|
|
form, contentType, err := createForm(values, "111.md")
|
|
if err != nil {
|
|
log.Fatal("Failed to create multipart form: %v", err)
|
|
}
|
|
headers := make(map[string][]string)
|
|
headers["Content-Type"] = []string{contentType}
|
|
// headers["Authorization"] = []string{"Token " + accessToken.(string)}
|
|
status, body, err := HttpCommon("POST", url, headers, form)
|
|
|
|
log.Printf("[%d]upload status: %d return body: %s error: %v\n", i, status, string(body), err)
|
|
group.Done()
|
|
}(i)
|
|
}
|
|
group.Wait()
|
|
}
|
|
|
|
func createForm(values map[string]io.Reader, name string) (io.Reader, string, error) {
|
|
buf := new(bytes.Buffer)
|
|
w := multipart.NewWriter(buf)
|
|
defer w.Close()
|
|
|
|
for k, v := range values {
|
|
var fw io.Writer
|
|
var err error
|
|
if k == "file" {
|
|
if fw, err = w.CreateFormFile(k, name); err != nil {
|
|
return nil, "", err
|
|
}
|
|
} else {
|
|
if fw, err = w.CreateFormField(k); err != nil {
|
|
return nil, "", err
|
|
}
|
|
}
|
|
if _, err = io.Copy(fw, v); err != nil {
|
|
return nil, "", err
|
|
}
|
|
}
|
|
|
|
return buf, w.FormDataContentType(), nil
|
|
}
|
|
|
|
func HttpCommon(method, url string, header map[string][]string, reader io.Reader) (int, []byte, error) {
|
|
req, err := http.NewRequest(method, url, reader)
|
|
if err != nil {
|
|
return -1, nil, err
|
|
}
|
|
req.Header = header
|
|
|
|
rsp, err := http.DefaultClient.Do(req)
|
|
if err != nil {
|
|
return -1, nil, err
|
|
}
|
|
defer rsp.Body.Close()
|
|
|
|
if rsp.StatusCode == http.StatusNotFound {
|
|
return rsp.StatusCode, nil, fmt.Errorf("url %s not found", url)
|
|
}
|
|
body, err := io.ReadAll(rsp.Body)
|
|
if err != nil {
|
|
return rsp.StatusCode, nil, err
|
|
}
|
|
|
|
return rsp.StatusCode, body, nil
|
|
}
|
|
|
|
func getToken() string {
|
|
url := fmt.Sprintf("%s:8000/api2/auth-token/", options.server)
|
|
header := make(map[string][]string)
|
|
header["Content-Type"] = []string{"application/x-www-form-urlencoded"}
|
|
data := []byte(fmt.Sprintf("username=%s&password=%s", options.username, options.password))
|
|
_, body, err := HttpCommon("POST", url, header, bytes.NewReader(data))
|
|
if err != nil {
|
|
return ""
|
|
}
|
|
tokenMap := make(map[string]interface{})
|
|
err = json.Unmarshal(body, &tokenMap)
|
|
if err != nil {
|
|
return ""
|
|
}
|
|
token, _ := tokenMap["token"].(string)
|
|
return token
|
|
}
|