web-dev-qa-db-fra.com

Un moyen simple de copier un fichier dans Golang

Existe-t-il un moyen simple/rapide de copier un fichier dans Go?

Je ne pouvais pas trouver un moyen rapide dans le Doc et la recherche sur Internet ne m'aide pas aussi.

67
herb

Avertissement: Cette réponse concerne principalement l’ajout d’un deuxième lien à un fichier, et non la copie du contenu.

Une copie robuste et efficace est simple sur le plan conceptuel, mais pas simple à mettre en œuvre en raison de la nécessité de gérer un certain nombre de cas Edge et de limitations système imposées par le système d'exploitation cible. et sa configuration.

Si vous souhaitez simplement dupliquer le fichier existant, vous pouvez utiliser os.Link(srcName, dstName). Cela évite de devoir déplacer des octets dans l'application et économise de l'espace disque. Pour les gros fichiers, cela représente un gain de temps et d’espace considérable.

Mais différents systèmes d'exploitation ont des restrictions différentes sur le fonctionnement des liens physiques. En fonction de votre application et de la configuration de votre système cible, les appels Link() peuvent ne pas fonctionner dans tous les cas.

Si vous voulez une seule fonction de copie générique, robuste et efficace, mettez à jour Copy() pour:

  1. Effectuez des vérifications pour vous assurer qu'au moins une forme de copie réussira (autorisations d'accès, répertoires existants, etc.)
  2. Vérifiez si les deux fichiers existent déjà et sont identiques en utilisant os.SameFile, Renvoyez succès s'ils sont identiques
  3. Tentative de lien, retour si succès
  4. Copier les octets (tous les moyens efficaces ont échoué), résultat renvoyé

Une optimisation consisterait à copier les octets dans une routine aller afin que l'appelant ne bloque pas la copie d'octet. Cela impose une complexité supplémentaire à l'appelant pour qu'il gère correctement le cas de réussite/d'erreur.

Si je voulais les deux, j'aurais deux fonctions de copie différentes: CopyFile(src, dst string) (error) pour une copie bloquante et CopyFileAsync(src, dst string) (chan c, error) qui renvoie un canal de signalisation à l'appelant pour le cas asynchrone.

package main

import (
    "fmt"
    "io"
    "os"
)

// CopyFile copies a file from src to dst. If src and dst files exist, and are
// the same, then return success. Otherise, attempt to create a hard link
// between the two files. If that fail, copy the file contents from src to dst.
func CopyFile(src, dst string) (err error) {
    sfi, err := os.Stat(src)
    if err != nil {
        return
    }
    if !sfi.Mode().IsRegular() {
        // cannot copy non-regular files (e.g., directories,
        // symlinks, devices, etc.)
        return fmt.Errorf("CopyFile: non-regular source file %s (%q)", sfi.Name(), sfi.Mode().String())
    }
    dfi, err := os.Stat(dst)
    if err != nil {
        if !os.IsNotExist(err) {
            return
        }
    } else {
        if !(dfi.Mode().IsRegular()) {
            return fmt.Errorf("CopyFile: non-regular destination file %s (%q)", dfi.Name(), dfi.Mode().String())
        }
        if os.SameFile(sfi, dfi) {
            return
        }
    }
    if err = os.Link(src, dst); err == nil {
        return
    }
    err = copyFileContents(src, dst)
    return
}

// copyFileContents copies the contents of the file named src to the file named
// by dst. The file will be created if it does not already exist. If the
// destination file exists, all it's contents will be replaced by the contents
// of the source file.
func copyFileContents(src, dst string) (err error) {
    in, err := os.Open(src)
    if err != nil {
        return
    }
    defer in.Close()
    out, err := os.Create(dst)
    if err != nil {
        return
    }
    defer func() {
        cerr := out.Close()
        if err == nil {
            err = cerr
        }
    }()
    if _, err = io.Copy(out, in); err != nil {
        return
    }
    err = out.Sync()
    return
}

func main() {
    fmt.Printf("Copying %s to %s\n", os.Args[1], os.Args[2])
    err := CopyFile(os.Args[1], os.Args[2])
    if err != nil {
        fmt.Printf("CopyFile failed %q\n", err)
    } else {
        fmt.Printf("CopyFile succeeded\n")
    }
}
63
markc

Vous avez tous les éléments nécessaires pour écrire une telle fonction dans la bibliothèque standard. Voici le code évident pour le faire.

// Copy the src file to dst. Any existing file will be overwritten and will not
// copy file attributes.
func Copy(src, dst string) error {
    in, err := os.Open(src)
    if err != nil {
        return err
    }
    defer in.Close()

    out, err := os.Create(dst)
    if err != nil {
        return err
    }
    defer out.Close()

    _, err = io.Copy(out, in)
    if err != nil {
        return err
    }
    return out.Close()
}
44
Paul Hankin

Si vous exécutez le code sous linux/mac, vous pouvez simplement exécuter la commande cp du système.

srcFolder := "copy/from/path"
destFolder := "copy/to/path"
cpCmd := exec.Command("cp", "-rf", srcFolder, destFolder)
err := cpCmd.Run()

C'est traiter un peu comme un script, mais le travail est fait. En outre, vous devez importer "os/exec"

11
Dandalf
import (
    "io/ioutil"
    "log"
)

func checkErr(err error) {
    if err != nil {
        log.Fatal(err)
    }
}

func copy(src string, dst string) {
    // Read all content of src to data
    data, err := ioutil.ReadFile(src)
    checkErr(err)
    // Write data to dst
    err = ioutil.WriteFile(dst, data, 0644)
    checkErr(err)
}
9
haosdent

Dans ce cas, il y a quelques conditions à vérifier, je préfère le code non imbriqué

func Copy(src, dst string) (int64, error) {
  src_file, err := os.Open(src)
  if err != nil {
    return 0, err
  }
  defer src_file.Close()

  src_file_stat, err := src_file.Stat()
  if err != nil {
    return 0, err
  }

  if !src_file_stat.Mode().IsRegular() {
    return 0, fmt.Errorf("%s is not a regular file", src)
  }

  dst_file, err := os.Create(dst)
  if err != nil {
    return 0, err
  }
  defer dst_file.Close()
  return io.Copy(dst_file, src_file)
}
2
edap

Si vous êtes sur Windows, vous pouvez envelopper CopyFileW comme ceci:

package utils

import (
    "syscall"
    "unsafe"
)

var (
    modkernel32   = syscall.NewLazyDLL("kernel32.dll")
    procCopyFileW = modkernel32.NewProc("CopyFileW")
)

// CopyFile wraps windows function CopyFileW
func CopyFile(src, dst string, failIfExists bool) error {
    lpExistingFileName, err := syscall.UTF16PtrFromString(src)
    if err != nil {
        return err
    }

    lpNewFileName, err := syscall.UTF16PtrFromString(dst)
    if err != nil {
        return err
    }

    var bFailIfExists uint32
    if failIfExists {
        bFailIfExists = 1
    } else {
        bFailIfExists = 0
    }

    r1, _, err := syscall.Syscall(
        procCopyFileW.Addr(),
        3,
        uintptr(unsafe.Pointer(lpExistingFileName)),
        uintptr(unsafe.Pointer(lpNewFileName)),
        uintptr(bFailIfExists))

    if r1 == 0 {
        return err
    }
    return nil
}

Le code est inspiré des wrappers dans C:\Go\src\syscall\zsyscall_windows.go

0
js.mouret

Voici un moyen évident de copier un fichier:

package main
import (
    "os"
    "log"
    "io"
)

func main() {
    sFile, err := os.Open("test.txt")
    if err != nil {
        log.Fatal(err)
    }
    defer sFile.Close()

    eFile, err := os.Create("test_copy.txt")
    if err != nil {
        log.Fatal(err)
    }
    defer eFile.Close()

    _, err = io.Copy(eFile, sFile) // first var shows number of bytes
    if err != nil {
        log.Fatal(err)
    }

    err = eFile.Sync()
    if err != nil {
        log.Fatal(err)
    }
}
0
Salvador Dali

Vous pouvez utiliser "exec". exec.Command ("cmd", "/ c", "copy", "fileToBeCopied destinationDirectory") pour Windows, je l'ai utilisé ainsi que son bon fonctionnement. Vous pouvez vous référer au manuel pour plus de détails sur exec.

0
jigar137