Simple way to copy a file

FileCopyGo

File Problem Overview


Is there any simple/fast way to copy a file in Go?

I couldn't find a fast way in the Doc's and searching the internet doesn't help as well.

File Solutions


Solution 1 - File

> Warning: This answer is mainly about adding a hard link to a file, not about copying the contents.

A robust and efficient copy is conceptually simple, but not simple to implement due to the need to handle a number of edge cases and system limitations that are imposed by the target operating system and it's configuration.

If you simply want to make a duplicate of the existing file you can use os.Link(srcName, dstName). This avoids having to move bytes around in the application and saves disk space. For large files, this is a significant time and space saving.

But various operating systems have different restrictions on how hard links work. Depending on your application and your target system configuration, Link() calls may not work in all cases.

If you want a single generic, robust and efficient copy function, update Copy() to:

  1. Perform checks to ensure that at least some form of copy will succeed (access permissions, directories exist, etc.)
  2. Check to see if both files already exist and are the same using os.SameFile, return success if they are the same
  3. Attempt a Link, return if success
  4. Copy the bytes (all efficient means failed), return result

An optimization would be to copy the bytes in a go routine so the caller doesn't block on the byte copy. Doing so imposes additional complexity on the caller to handle the success/error case properly.

If I wanted both, I would have two different copy functions: CopyFile(src, dst string) (error) for a blocking copy and CopyFileAsync(src, dst string) (chan c, error) which passes a signaling channel back to the caller for the asynchronous case.

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")
	}
}

Solution 2 - File

You've got all the bits you need to write such a function in the standard library. Here's the obvious code to do it.

// 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()
}

Solution 3 - File

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)
}

Solution 4 - File

If you are running the code in linux/mac, you could just execute the system's cp command.

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

It's treating go a bit like a script, but it gets the job done. Also, you need to import "os/exec"

Solution 5 - File

Starting with Go 1.15 (Aug 2020), you can use File.ReadFrom:

package main
import "os"

func main() {
   r, err := os.Open("in.txt")
   if err != nil {
      panic(err)
   }
   defer r.Close()
   w, err := os.Create("out.txt")
   if err != nil {
      panic(err)
   }
   defer w.Close()
   w.ReadFrom(r)
}

Solution 6 - File

In this case there are a couple of conditions to verify, I prefer non-nested 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)
}

Solution 7 - File

Here is an obvious way to copy a 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)
    }
}

Solution 8 - File

If you are on windows, you can wrap CopyFileW like this:

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 is inspired by wrappers in C:\Go\src\syscall\zsyscall_windows.go

Solution 9 - File

You can use "exec". exec.Command("cmd","/c","copy","fileToBeCopied destinationDirectory") for windows I have used this and its working fine. You can refer manual for more details on exec.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionherbView Question on Stackoverflow
Solution 1 - FilemarkcView Answer on Stackoverflow
Solution 2 - FilePaul HankinView Answer on Stackoverflow
Solution 3 - FilehaosdentView Answer on Stackoverflow
Solution 4 - FileDandalfView Answer on Stackoverflow
Solution 5 - FileZomboView Answer on Stackoverflow
Solution 6 - FileedapView Answer on Stackoverflow
Solution 7 - FileSalvador DaliView Answer on Stackoverflow
Solution 8 - Filejs.mouretView Answer on Stackoverflow
Solution 9 - Filejigar137View Answer on Stackoverflow