Moyen Simple de copier un fichier dans Golang
Existe-t-il un moyen simple/rapide de copier un fichier dans Go?
Je n'ai pas pu trouver un moyen rapide dans les documents et la recherche sur internet n'aide pas aussi bien.
8 réponses
Une copierobuste etefficace est conceptuellement simple, mais pas simple à implémenter en raison de la nécessité de gérer un certain nombre de cas périphériques et de limitations système imposées par le système d'exploitation cible et sa configuration.
Si vous voulez simplement faire une copie du fichier existant, vous pouvez utiliser os.Link(srcName, dstName)
. Cela évite d'avoir à déplacer d'octets dans l'application et économise de l'espace disque. Pour les fichiers volumineux, c'est un temps et un espace importants économie.
Mais différents systèmes d'exploitation ont des restrictions différentes sur la façon dont les liens durs fonctionnent. Selon votre application et 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()
vers:
- effectuer des vérifications pour s'assurer qu'au moins une forme de copie réussira (autorisations d'accès, répertoires existent, etc.)
- vérifiez si les deux fichiers existent déjà et sont les mêmes en utilisant
os.SameFile
, Retour succès si elles sont les mêmes - tentative D'un lien, retour en cas de succès
- Copiez les octets (tous les moyens efficaces ont échoué), renvoyez le résultat
Une optimisation consisterait à copier les octets dans une routine go afin que l'appelant ne bloque pas la copie d'octets. Cela impose une complexité supplémentaire à l'appelant pour gérer correctement le cas de réussite / erreur.
Si je voulais les deux, j'aurais deux fonctions de copie différentes: CopyFile(src, dst string) (error)
pour une copie bloquante et {[6] } qui transmet 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")
}
}
Vous avez tous les éléments dont vous avez besoin 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()
}
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)
}
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()
Il traite go un peu comme un script, mais il fait le travail. En outre, vous devez importer "os / exec"
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)
}
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)
}
}
Si vous êtes sous 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
Regardez aller-shutil.
Mais sachez que cela ne copie pas les métadonnées. Aussi besoin de quelqu'un pour mettre en œuvre des choses comme déplacer.
Pourrait valoir la peine d'utiliser exec.