Gibt es eine einfache / schnelle Möglichkeit, eine Datei in Go zu kopieren?
Ich konnte keinen schnellen Weg in den Dokumenten finden und das Durchsuchen des Internets hilft auch nicht.
Gibt es eine einfache / schnelle Möglichkeit, eine Datei in Go zu kopieren?
Ich konnte keinen schnellen Weg in den Dokumenten finden und das Durchsuchen des Internets hilft auch nicht.
Antworten:
Warnung: Bei dieser Antwort geht es hauptsächlich um das Hinzufügen eines zweiten Links zu einer Datei, nicht um das Kopieren des Inhalts.
Eine robuste und effiziente Kopie ist konzeptionell einfach, aber nicht einfach zu implementieren, da eine Reihe von Randfällen und Systemeinschränkungen behandelt werden müssen, die vom Zielbetriebssystem und seiner Konfiguration auferlegt werden.
Wenn Sie einfach ein Duplikat der vorhandenen Datei erstellen möchten, können Sie diese verwenden os.Link(srcName, dstName)
. Dies vermeidet das Verschieben von Bytes in der Anwendung und spart Speicherplatz. Bei großen Dateien ist dies eine erhebliche Zeit- und Platzersparnis.
Verschiedene Betriebssysteme haben jedoch unterschiedliche Einschränkungen hinsichtlich der Funktionsweise von Hardlinks. Abhängig von Ihrer Anwendung und Ihrer Zielsystemkonfiguration funktionieren Link()
Anrufe möglicherweise nicht in allen Fällen.
Wenn Sie eine einzelne generische, robuste und effiziente Kopierfunktion wünschen, aktualisieren Sie Copy()
auf:
os.SameFile
Geben Sie den Erfolg zurück, wenn sie identisch sindEine Optimierung wäre, die Bytes in einer Go-Routine zu kopieren, damit der Aufrufer die Byte-Kopie nicht blockiert. Dies führt zu einer zusätzlichen Komplexität des Anrufers, um den Erfolgs- / Fehlerfall ordnungsgemäß zu behandeln.
Wenn ich beides wollte, hätte ich zwei verschiedene Kopierfunktionen: CopyFile(src, dst string) (error)
für eine blockierende Kopie, CopyFileAsync(src, dst string) (chan c, error)
die einen Signalisierungskanal für den asynchronen Fall an den Anrufer zurückgibt.
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")
}
}
if err = os.Link(src, dst)...
Diese Funktion funktioniert nicht wie sie ist für Sicherungszwecke. Wenn Sie Dateien kopieren möchten, um eine Sicherungskopie einiger Daten zu erstellen, müssen Sie die Daten selbst auf das Dateisystem kopieren
Sie haben alle Bits, die Sie benötigen, um eine solche Funktion in die Standardbibliothek zu schreiben. Hier ist der offensichtliche Code dafür.
// 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()
}
out.Close()
immer scheitern? Sie suchen nicht nach dem Fehler, aber die Dokumente sagen, dass aufeinanderfolgende Aufrufe von Close()
fehlschlagen.
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)
}
Wenn Sie den Code unter Linux / Mac ausführen, können Sie einfach den Befehl cp des Systems ausführen.
srcFolder := "copy/from/path"
destFolder := "copy/to/path"
cpCmd := exec.Command("cp", "-rf", srcFolder, destFolder)
err := cpCmd.Run()
Es behandelt Go ein bisschen wie ein Skript, aber es erledigt den Job. Außerdem müssen Sie "os / exec" importieren.
--help
mit Ihrem Code zu kopieren , aber nichts ist passiert. ;)
In diesem Fall müssen einige Bedingungen überprüft werden. Ich bevorzuge nicht verschachtelten Code
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)
}
Ab Go 1.15 (August 2020) können Sie File.ReadFrom verwenden :
package main
import (
"log"
"os"
)
func main() {
in_o, e := os.Open("a.go")
if e != nil {
log.Fatal(e)
}
out_o, e := os.Create("b.go")
if e != nil {
log.Fatal(e)
}
out_o.ReadFrom(in_o)
}
Hier ist eine offensichtliche Möglichkeit, eine Datei zu kopieren:
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)
}
}
e
in eFile
?
Wenn Sie unter Windows arbeiten, können Sie CopyFileW wie folgt umbrechen:
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
}
Code ist von Wrappern in inspiriert C:\Go\src\syscall\zsyscall_windows.go
Schauen Sie sich go-shutil an .
Beachten Sie jedoch, dass keine Metadaten kopiert werden. Brauche auch jemanden, der Dinge wie Umzug umsetzt.
Könnte sich lohnen, nur exec zu verwenden.