/
githubmirror
/
rclone
Обзор
Документация
Войти
/
githubmirror
/
rclone
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
cmd/copyurl/copyurl.go
187 строк
6 KB
Jack Kelly
copyurl: Extend copyurl docs with an example of CSV FILENAMEs starting with a path.
17 фев 2026, 14:46
17 фев 2026, 14:46
64d6916
Код
Авторство
О чём код?
// Package copyurl provides the copyurl command. package copyurl import ( "context" "encoding/csv" "errors" "fmt" "os" "strings" "github.com/rclone/rclone/cmd" "github.com/rclone/rclone/fs" "github.com/rclone/rclone/fs/config/flags" "github.com/rclone/rclone/fs/operations" "github.com/rclone/rclone/lib/errcount" "github.com/spf13/cobra" "golang.org/x/sync/errgroup" ) var ( autoFilename = false headerFilename = false printFilename = false stdout = false noClobber = false urls = false ) func init() { cmd.Root.AddCommand(commandDefinition) cmdFlags := commandDefinition.Flags() flags.BoolVarP(cmdFlags, &autoFilename, "auto-filename", "a", autoFilename, "Get the file name from the URL and use it for destination file path", "") flags.BoolVarP(cmdFlags, &headerFilename, "header-filename", "", headerFilename, "Get the file name from the Content-Disposition header", "") flags.BoolVarP(cmdFlags, &printFilename, "print-filename", "p", printFilename, "Print the resulting name from --auto-filename", "") flags.BoolVarP(cmdFlags, &noClobber, "no-clobber", "", noClobber, "Prevent overwriting file with same name", "") flags.BoolVarP(cmdFlags, &stdout, "stdout", "", stdout, "Write the output to stdout rather than a file", "") flags.BoolVarP(cmdFlags, &urls, "urls", "", stdout, "Use a CSV file of links to process multiple URLs", "") } var commandDefinition = &cobra.Command{ Use: "copyurl https://example.com dest:path", Short: `Copy the contents of the URL supplied content to dest:path.`, Long: strings.ReplaceAll(`Download a URL's content and copy it to the destination without saving it in temporary storage. Setting |--auto-filename| will attempt to automatically determine the filename from the URL (after any redirections) and used in the destination path. With |--header-filename| in addition, if a specific filename is set in HTTP headers, it will be used instead of the name from the URL. With |--print-filename| in addition, the resulting file name will be printed. Setting |--no-clobber| will prevent overwriting file on the destination if there is one with the same name. Setting |--stdout| or making the output file name |-| will cause the output to be written to standard output. Setting |--urls| allows you to input a CSV file of URLs in format: URL, FILENAME. If |--urls| is in use then replace the URL in the arguments with the file containing the URLs, e.g.: |||sh rclone copyurl --urls myurls.csv remote:dir ||| Missing filenames will be autogenerated equivalent to using |--auto-filename|. Note that |--stdout| and |--print-filename| are incompatible with |--urls|. This will do |--transfers| copies in parallel. Note that if |--auto-filename| is desired for all URLs then a file with only URLs and no filename can be used. Each FILENAME in the CSV file can start with a relative path which will be appended to the destination path provided at the command line. For example, running the command shown above with the following CSV file will write two files to the destination: |remote:dir/local/path/bar.json| and |remote:dir/another/local/directory/qux.json| |||csv https://example.org/foo/bar.json,local/path/bar.json https://example.org/qux/baz.json,another/local/directory/qux.json ||| ### Troubleshooting If you can't get |rclone copyurl| to work then here are some things you can try: - |--disable-http2| rclone will use HTTP2 if available - try disabling it - |--bind 0.0.0.0| rclone will use IPv6 if available - try disabling it - |--bind ::0| to disable IPv4 - |--user agent curl| - some sites have whitelists for curl's user-agent - try that - Make sure the site works with |curl| directly`, "|", "`"), Annotations: map[string]string{ "versionIntroduced": "v1.43", "groups": "Important", }, RunE: func(command *cobra.Command, args []string) (err error) { cmd.CheckArgs(1, 2, command, args) cmd.Run(true, true, command, func() error { if !urls { return run(args) } return runURLS(args) }) return nil }, } var copyURL = operations.CopyURL // for testing // runURLS processes a .csv file of urls and filenames func runURLS(args []string) (err error) { if stdout { return errors.New("can't use --stdout with --urls") } if printFilename { return errors.New("can't use --print-filename with --urls") } dstFs := cmd.NewFsDir(args[1:]) f, err := os.Open(args[0]) if err != nil { return fmt.Errorf("failed to open .csv file: %w", err) } defer fs.CheckClose(f, &err) reader := csv.NewReader(f) reader.FieldsPerRecord = -1 urlList, err := reader.ReadAll() if err != nil { return fmt.Errorf("failed reading .csv file: %w", err) } ec := errcount.New() g, gCtx := errgroup.WithContext(context.Background()) ci := fs.GetConfig(gCtx) g.SetLimit(ci.Transfers) for _, urlEntry := range urlList { if len(urlEntry) == 0 { continue } g.Go(func() error { url := urlEntry[0] var filename string if len(urlEntry) > 1 { filename = urlEntry[1] } _, err := copyURL(gCtx, dstFs, filename, url, filename == "", headerFilename, noClobber) if err != nil { fs.Errorf(filename, "failed to copy URL %q: %v", url, err) ec.Add(err) } return nil }) } ec.Add(g.Wait()) return ec.Err("not all URLs copied successfully") } // run runs the command for a single URL func run(args []string) error { var err error var dstFileName string var fsdst fs.Fs if !stdout { if len(args) < 2 { return errors.New("need 2 arguments if not using --stdout") } if args[1] == "-" { stdout = true } else if autoFilename { fsdst = cmd.NewFsDir(args[1:]) } else { fsdst, dstFileName = cmd.NewFsDstFile(args[1:]) } } var dst fs.Object if stdout { err = operations.CopyURLToWriter(context.Background(), args[0], os.Stdout) } else { dst, err = copyURL(context.Background(), fsdst, dstFileName, args[0], autoFilename, headerFilename, noClobber) if printFilename && err == nil && dst != nil { fmt.Println(dst.Remote()) } } return err }