Ответ 1
Предупреждение: Этот ответ в основном о добавлении второй ссылки в файл, а не о копировании содержимого.
Надежная и эффективная копия концептуально проста, но ее непросто реализовать из-за необходимости обрабатывать ряд крайних случаев и системных ограничений, налагаемых целевой операционной системой и ее конфигурацией.
Если вы просто хотите сделать копию существующего файла, вы можете использовать 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")
}
}