Есть ли простой/быстрый способ скопировать файл в Go?
Я не мог найти быстрый способ в Doc, и поиск в Интернете тоже не помогает.
Есть ли простой/быстрый способ скопировать файл в Go?
Я не мог найти быстрый способ в Doc, и поиск в Интернете тоже не помогает.
Предупреждение: Этот ответ в основном о добавлении второй ссылки в файл, а не о копировании содержимого.
Надежная и эффективная копия концептуально проста, но ее непросто реализовать из-за необходимости обрабатывать ряд крайних случаев и системных ограничений, налагаемых целевой операционной системой и ее конфигурацией.
Если вы просто хотите сделать копию существующего файла, вы можете использовать os.Link(srcName, dstName)
. Это избавляет от необходимости перемещать байты в приложении и экономит место на диске. Для больших файлов это значительная экономия времени и места.
Но различные операционные системы имеют разные ограничения на работу жестких ссылок. В зависимости от вашего приложения и конфигурации вашей целевой системы, вызовы Link()
могут работать не во всех случаях.
Если вам нужна единая, надежная и эффективная функция копирования, обновите функцию Copy()
образом:
os.SameFile
, верните успех, если они совпадаютОптимизация заключалась бы в копировании байтов в подпрограмме go, чтобы вызывающая сторона не блокировала байтовую копию. Это создает дополнительную сложность для вызывающей стороны, чтобы правильно обрабатывать случай успеха/ошибки.
Если бы я хотел и то и другое, у меня были бы две разные функции копирования: CopyFile(src, dst string) (error)
для блокирующей копии и CopyFileAsync(src, dst string) (chan c, error)
которая передает канал сигнализации обратно вызывающей стороне. для асинхронного случая.
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 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")
}
}
У вас есть все необходимые вам биты для записи такой функции в стандартной библиотеке. Здесь очевидный код для этого.
// 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()
}
Если вы используете код в linux/mac, вы можете просто выполнить команду cp system.
srcFolder := "copy/from/path"
destFolder := "copy/to/path"
cpCmd := exec.Command("cp", "-rf", srcFolder, destFolder)
err := cpCmd.Run()
Это лечение немного похоже на script, но оно выполняет свою работу. Кроме того, вам нужно импортировать "os/exec"
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)
}
В этом случае существует несколько условий для проверки, я предпочитаю не вложенный код
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)
}
Вот простой способ скопировать файл:
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)
}
}
Если вы находитесь в окнах, вы можете обернуть CopyFileW следующим образом:
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
}
Код вдохновлен обертками в C:\Go\src\syscall\zsyscall_windows.go
Вы можете использовать "exec". exec.Command("cmd", "/c", "copy", "fileToBeCopied destinationDirectory") для окон, которые я использовал, и это прекрасно работает. Вы можете обратиться к руководству для более подробной информации о Exec.
Посмотрите go-shutil.
Но имейте в виду, что он не копирует метаданные. Также нужно, чтобы кто-то выполнял такие вещи, как move.
Возможно, стоит просто использовать exec.