Add actual file upload to Yandex with progress bar
This commit is contained in:
@@ -12,6 +12,7 @@ import (
|
||||
const YANDEX_APPID = "7d8a0561fdc44c05bb6695b464403f9c"
|
||||
const YANDEX_APPPW = "56e12e4ed0d64738bf441a47f68c7146"
|
||||
const DEVICE_NAME = "yapusher-cli"
|
||||
const MAX_FILE_SIZE = 10 * 1024 * 1024 * 1024 // 10 gigabytes
|
||||
|
||||
var (
|
||||
c *context.Context
|
||||
@@ -34,16 +35,23 @@ func New(cc *context.Context) {
|
||||
Name: "uploadPath",
|
||||
Description: "Path to upload your file on Yandex.Disk. Must exist before uploading.",
|
||||
Type: "string",
|
||||
DefaultValue: "/",
|
||||
DefaultValue: "",
|
||||
})
|
||||
|
||||
_ = c.Flagger.AddFlag(&flagger.Flag{
|
||||
Name: "file",
|
||||
Description: "Path to file that will be uploaded. Max upload size - 50 GB",
|
||||
Description: "Path to file that will be uploaded. Max upload size - 10 GB",
|
||||
Type: "string",
|
||||
DefaultValue: "",
|
||||
})
|
||||
|
||||
_ = c.Flagger.AddFlag(&flagger.Flag{
|
||||
Name: "force",
|
||||
Description: "Force file to be uploaded even if destination file on Yandex.Disk already exists.",
|
||||
Type: "bool",
|
||||
DefaultValue: false,
|
||||
})
|
||||
|
||||
dlog.Info().Msg("Domain initialized")
|
||||
}
|
||||
|
||||
@@ -56,7 +64,9 @@ func Process() {
|
||||
|
||||
filePath, _ := c.Flagger.GetStringValue("file")
|
||||
if filePath != "" {
|
||||
uploadFile()
|
||||
uploadPath, _ := c.Flagger.GetStringValue("uploadPath")
|
||||
forceUpload, _ := c.Flagger.GetBoolValue("force")
|
||||
uploadFile(uploadPath, filePath, forceUpload)
|
||||
}
|
||||
|
||||
if !checkAuth() {
|
||||
|
@@ -5,13 +5,18 @@ package yandexv1
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/schollz/progressbar/v2"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func reportFileProgress(r int64) {
|
||||
}
|
||||
|
||||
func sendCode(code int) {
|
||||
baseURL := "https://oauth.yandex.ru/token"
|
||||
|
||||
@@ -51,11 +56,108 @@ func sendCode(code int) {
|
||||
dlog.Error().Err(err).Msg("Failed to decode response")
|
||||
}
|
||||
|
||||
dlog.Error().Interface("response", errorData).Msg("Got error from Yandex, not authorized. Please retry authorization")
|
||||
dlog.Error().Str("error", errorData.Error).Str("description", errorData.ErrorDescription).Msg("Got error from Yandex, not authorized. Please retry authorization")
|
||||
authorize()
|
||||
}
|
||||
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
func uploadFile() {}
|
||||
func uploadFile(uploadPath string, filePath string, overwriteFile bool) {
|
||||
uploadRequestURL := "https://cloud-api.yandex.net/v1/disk/resources/upload"
|
||||
|
||||
// Checking file existence before requesting
|
||||
normalizedFilePath, _ := filepath.Abs(filePath)
|
||||
|
||||
fileInfo, err := os.Stat(normalizedFilePath)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
dlog.Fatal().Err(err).Msg("File for uploading not found")
|
||||
} else {
|
||||
dlog.Fatal().Err(err).Msg("Failed to stat uploading file")
|
||||
}
|
||||
}
|
||||
|
||||
if fileInfo.Size() > (MAX_FILE_SIZE - 1) {
|
||||
dlog.Fatal().Msg("Requested file is too big")
|
||||
}
|
||||
|
||||
if !fileInfo.Mode().IsRegular() {
|
||||
dlog.Fatal().Msg("Only regular files uploading is supported right now")
|
||||
}
|
||||
|
||||
client := http.Client{}
|
||||
uploadInfo := UploadInfo{}
|
||||
|
||||
// The first request will get from Yandex upload URL
|
||||
req, _ := http.NewRequest("GET", uploadRequestURL, nil)
|
||||
req.Header.Add("Content-Type", "application/json")
|
||||
req.Header.Add("Authorization", "OAuth "+c.Config.Token.AccessToken)
|
||||
|
||||
query := url.Values{}
|
||||
query.Add("path", "disk:/"+uploadPath+"/"+fileInfo.Name())
|
||||
query.Add("overwrite", strconv.FormatBool(overwriteFile))
|
||||
|
||||
req.URL.RawQuery = query.Encode()
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
dlog.Fatal().Err(err).Msg("Failed to send request")
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode == http.StatusOK {
|
||||
err = json.NewDecoder(resp.Body).Decode(&uploadInfo)
|
||||
if err != nil {
|
||||
dlog.Error().Err(err).Msg("Failed to decode response")
|
||||
}
|
||||
} else {
|
||||
errorData := UploadError{}
|
||||
err = json.NewDecoder(resp.Body).Decode(&errorData)
|
||||
if err != nil {
|
||||
dlog.Error().Err(err).Msg("Failed to decode response")
|
||||
}
|
||||
|
||||
dlog.Info().Str("error", errorData.Error).Str("description", errorData.Description).Msg("Failed to upload file")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
file, _ := os.Open(normalizedFilePath)
|
||||
bar := progressbar.NewOptions(
|
||||
int(fileInfo.Size()),
|
||||
progressbar.OptionSetBytes(int(fileInfo.Size())),
|
||||
progressbar.OptionSetRenderBlankState(true),
|
||||
)
|
||||
|
||||
progressReader := &progressReader{
|
||||
r: file,
|
||||
progressbar: bar,
|
||||
}
|
||||
|
||||
if uploadInfo.URL == "" {
|
||||
dlog.Fatal().Msg("Got empty upload URL. Report a bug at https://github.com/fat0troll/yapusher/issues because this situation is impossible.")
|
||||
}
|
||||
|
||||
uploadReq, _ := http.NewRequest("PUT", uploadInfo.URL, progressReader)
|
||||
|
||||
uploadResp, err := client.Do(uploadReq)
|
||||
if err != nil {
|
||||
dlog.Fatal().Err(err).Msg("Failed to send upload request")
|
||||
}
|
||||
defer uploadResp.Body.Close()
|
||||
|
||||
switch uploadResp.StatusCode {
|
||||
case http.StatusCreated:
|
||||
dlog.Info().Msg("File uploaded successfully")
|
||||
case http.StatusAccepted:
|
||||
dlog.Info().Msg("File uploaded successfully, but it will take time for Yandex.Disk to handle it internally. Be patient and don't try to upload single file many times")
|
||||
case http.StatusRequestEntityTooLarge:
|
||||
dlog.Fatal().Msg("File upload is too large. Report a bug at https://github.com/fat0troll/yapusher/issues because this situation should be handled before upload attempt.")
|
||||
case http.StatusInsufficientStorage:
|
||||
dlog.Fatal().Msg("There is no space left on your Yandex.Disk.")
|
||||
default:
|
||||
dlog.Fatal().Msg("Failed to upload file (error on Yandex's side). Try again later.")
|
||||
}
|
||||
|
||||
os.Exit(0)
|
||||
}
|
||||
|
@@ -3,7 +3,46 @@
|
||||
|
||||
package yandexv1
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/schollz/progressbar/v2"
|
||||
"io"
|
||||
)
|
||||
|
||||
type progressReader struct {
|
||||
r io.Reader
|
||||
atEOF bool
|
||||
progressbar *progressbar.ProgressBar
|
||||
}
|
||||
|
||||
func (pr *progressReader) Read(p []byte) (int, error) {
|
||||
n, err := pr.r.Read(p)
|
||||
if err == io.EOF {
|
||||
pr.atEOF = true
|
||||
}
|
||||
pr.report(int64(n))
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (pr *progressReader) report(n int64) {
|
||||
_ = pr.progressbar.Add64(n)
|
||||
if pr.atEOF {
|
||||
fmt.Print("\n\n")
|
||||
}
|
||||
}
|
||||
|
||||
type TokenError struct {
|
||||
Error string `json:"error"`
|
||||
ErrorDescription string `json:"error_description"`
|
||||
}
|
||||
|
||||
type UploadError struct {
|
||||
Error string `json:"error"`
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
type UploadInfo struct {
|
||||
URL string `json:"href"`
|
||||
Method string `json:"method"`
|
||||
URLIsTemplated bool `json:"templated"`
|
||||
}
|
||||
|
Reference in New Issue
Block a user