191 lines
4.7 KiB
Go
191 lines
4.7 KiB
Go
/*
|
|
* Copyright 2020 The Dragonfly Authors
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
package dfget
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"time"
|
|
|
|
"d7y.io/dragonfly/v2/cdnsystem/source"
|
|
"d7y.io/dragonfly/v2/client/clientutil/progressbar"
|
|
"d7y.io/dragonfly/v2/client/config"
|
|
"d7y.io/dragonfly/v2/pkg/basic"
|
|
"d7y.io/dragonfly/v2/pkg/dferrors"
|
|
logger "d7y.io/dragonfly/v2/pkg/dflog"
|
|
"d7y.io/dragonfly/v2/pkg/rpc/base"
|
|
dfdaemongrpc "d7y.io/dragonfly/v2/pkg/rpc/dfdaemon"
|
|
|
|
// Init daemon rpc client
|
|
_ "d7y.io/dragonfly/v2/pkg/rpc/dfdaemon/client"
|
|
dfclient "d7y.io/dragonfly/v2/pkg/rpc/dfdaemon/client"
|
|
"github.com/go-http-utils/headers"
|
|
)
|
|
|
|
var filter string
|
|
|
|
func Download(cfg *config.DfgetConfig, client dfclient.DaemonClient) error {
|
|
var (
|
|
ctx = context.Background()
|
|
cancel context.CancelFunc
|
|
hdr = parseHeader(cfg.Header)
|
|
)
|
|
|
|
if client == nil {
|
|
return downloadFromSource(cfg, hdr)
|
|
}
|
|
|
|
output, err := filepath.Abs(cfg.Output)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if cfg.Timeout > 0 {
|
|
ctx, cancel = context.WithTimeout(ctx, cfg.Timeout)
|
|
} else {
|
|
ctx, cancel = context.WithCancel(ctx)
|
|
}
|
|
defer cancel()
|
|
|
|
request := &dfdaemongrpc.DownRequest{
|
|
Url: cfg.URL,
|
|
UrlMeta: &base.UrlMeta{
|
|
Digest: cfg.Digest,
|
|
Range: hdr[headers.Range],
|
|
Header: hdr,
|
|
},
|
|
Output: output,
|
|
BizId: cfg.CallSystem,
|
|
Filter: filter,
|
|
Uid: int64(basic.UserID),
|
|
Gid: int64(basic.UserGroup),
|
|
}
|
|
var (
|
|
start = time.Now()
|
|
end time.Time
|
|
)
|
|
down, err := client.Download(ctx, request)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
var (
|
|
result *dfdaemongrpc.DownResult
|
|
)
|
|
// todo using progressbar when showBar is true
|
|
pb := progressbar.DefaultBytes(-1, "Downloading")
|
|
for {
|
|
result, err = down.Recv()
|
|
if err != nil {
|
|
if de, ok := err.(*dferrors.DfError); ok {
|
|
logger.Errorf("dragonfly daemon returns error code %d/%s", de.Code, de.Message)
|
|
} else {
|
|
logger.Errorf("dragonfly daemon returns error %s", err)
|
|
}
|
|
break
|
|
}
|
|
if result.CompletedLength > 0 {
|
|
pb.Set64(int64(result.CompletedLength))
|
|
}
|
|
if result.Done {
|
|
pb.Describe("Downloaded")
|
|
pb.Finish()
|
|
end = time.Now()
|
|
fmt.Printf("Task: %s\nPeer: %s\n", result.TaskId, result.PeerId)
|
|
fmt.Printf("Download success, time cost: %dms, length: %d\n", end.Sub(start).Milliseconds(), result.CompletedLength)
|
|
break
|
|
}
|
|
}
|
|
if err != nil {
|
|
logger.Errorf("download by dragonfly error: %s", err)
|
|
return downloadFromSource(cfg, hdr)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func downloadFromSource(cfg *config.DfgetConfig, hdr map[string]string) (err error) {
|
|
if cfg.DisableBackSource {
|
|
err = fmt.Errorf("dfget download error, and back source disabled")
|
|
logger.Warnf("%s", err)
|
|
return err
|
|
}
|
|
|
|
var (
|
|
start = time.Now()
|
|
end time.Time
|
|
)
|
|
|
|
fmt.Println("dfget download error, try to download from source")
|
|
var (
|
|
resourceClient source.ResourceClient
|
|
target *os.File
|
|
response io.ReadCloser
|
|
_ map[string]string
|
|
written int64
|
|
)
|
|
|
|
resourceClient, err = source.NewSourceClient()
|
|
if err != nil {
|
|
logger.Errorf("init source client error: %s", err)
|
|
return err
|
|
}
|
|
|
|
response, _, err = resourceClient.Download(cfg.URL, hdr)
|
|
if err != nil {
|
|
logger.Errorf("download from source error: %s", err)
|
|
return err
|
|
}
|
|
defer response.Close()
|
|
|
|
target, err = os.OpenFile(cfg.Output, os.O_RDWR|os.O_CREATE, 0644)
|
|
if err != nil {
|
|
logger.Errorf("open %s error: %s", cfg.Output, err)
|
|
return err
|
|
}
|
|
|
|
written, err = io.Copy(target, response)
|
|
if err != nil {
|
|
logger.Errorf("copied %d bytes to %s, with error: %s", written, cfg.Output, err)
|
|
return err
|
|
}
|
|
logger.Infof("copied %d bytes to %s", written, cfg.Output)
|
|
end = time.Now()
|
|
fmt.Printf("Download from source success, time cost: %dms\n", end.Sub(start).Milliseconds())
|
|
|
|
// change permission
|
|
logger.Infof("change own to uid %d gid %d", basic.UserID, basic.UserGroup)
|
|
if err = os.Chown(cfg.Output, basic.UserID, basic.UserGroup); err != nil {
|
|
logger.Errorf("change own failed: %s", err)
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func parseHeader(s []string) map[string]string {
|
|
hdr := map[string]string{}
|
|
for _, h := range s {
|
|
idx := strings.Index(h, ":")
|
|
if idx > 0 {
|
|
hdr[h[:idx]] = strings.TrimLeft(h[idx:], " ")
|
|
}
|
|
}
|
|
return hdr
|
|
}
|