2022-05-14 17:47:23 +02:00

136 lines
2.9 KiB
Go

package main
import (
"crypto/sha256"
"encoding/hex"
"fmt"
"io"
"os"
"path/filepath"
"github.com/go-resty/resty/v2"
"github.com/schollz/progressbar/v3"
photosapi "gitlab.celogeek.com/photos/api/internal/photos/api"
)
type UploadCommand struct {
Url string `short:"u" long:"url" description:"Url of the instance" required:"true"`
Token string `short:"t" long:"token" description:"Token of the instance" required:"true"`
File string `short:"f" long:"file" description:"File to upload" required:"true"`
Workers uint32 `short:"w" long:"workers" description:"Number of workers for uploading chunks" default:"4"`
}
type UploadCreate struct {
UploadId string `json:"upload_id"`
}
type UploadPartResult struct {
UploadId string `json:"upload_id"`
Part uint `json:"part"`
Size uint `json:"size"`
PartSha256 string `json:"sha256"`
}
func (c *UploadCommand) Cli() *resty.Client {
return resty.New().SetBaseURL(c.Url).SetAuthScheme("Private").SetAuthToken(c.Token)
}
func (c *UploadCommand) Execute(args []string) error {
cli := c.Cli()
resp, err := cli.R().SetError(&ResponseError{}).SetResult(&UploadCreate{}).Post("/upload")
if err != nil {
return err
}
if resp.IsError() {
return resp.Error().(*ResponseError)
}
uploadId := resp.Result().(*UploadCreate).UploadId
f, err := os.Open(c.File)
if err != nil {
return err
}
defer f.Close()
st, err := f.Stat()
if err != nil {
return err
}
progress := progressbar.DefaultBytes(st.Size(), fmt.Sprintf("Uploading %s", filepath.Base(c.File)))
defer progress.Close()
tee := io.TeeReader(f, progress)
b := make([]byte, photosapi.MaxUploadPartSize)
parts := 0
completesha256 := sha256.New()
for {
n, err := tee.Read(b)
if err != nil {
if err == io.EOF {
break
} else {
return err
}
}
parts++
partsha256 := sha256.New()
partsha256.Write(b[:n])
completesha256.Write(b[:n])
resp, err := cli.
R().
SetError(&ResponseError{}).
SetResult(&UploadPartResult{}).
SetQueryParam("part", fmt.Sprint(parts)).
SetQueryParam("sha256", hex.EncodeToString(partsha256.Sum(nil))).
SetBody(b[:n]).
SetPathParam("id", uploadId).
Put("/upload/{id}")
if err != nil {
return err
}
if resp.IsError() {
return resp.Error().(*ResponseError)
}
}
fmt.Printf(
"Upload: %s\nParts: %d\n",
uploadId,
parts,
)
resp, err = cli.
R().
SetError(&ResponseError{}).
SetPathParam("id", uploadId).
SetBody(&photosapi.UploadCompleteRequest{
Sha256: hex.EncodeToString(completesha256.Sum(nil)),
Parts: uint(parts),
Name: filepath.Base(c.File),
}).
Post("/upload/{id}")
if err != nil {
return err
}
if resp.IsError() {
return resp.Error().(*ResponseError)
}
fmt.Printf("Response: %s\n", resp.Body())
cli.R().SetPathParam("id", uploadId).Delete("/upload/{id}")
return nil
}
func init() {
parser.AddCommand("upload", "Upload a file", "", &UploadCommand{})
}