web-dev-qa-db-ja.com

Golangでファイルをコピーする簡単な方法

Goでファイルをコピーする簡単な方法はありますか?

Docで高速な方法を見つけることができなかったので、インターネットを検索してもうまくいきません。

67
herb

警告:この回答は、ファイルへの2番目のリンクの追加に関するものであり、コンテンツのコピーに関するものではありません。

ロバストおよび効率的コピーは概念的には簡単ですが、多くのEdgeケースを処理する必要があるため、およびターゲットオペレーティングシステムによって課されるシステム制限のために実装が簡単ではありませんそしてそれは構成です。

既存のファイルの複製を作成するだけの場合は、os.Link(srcName, dstName)を使用できます。これにより、アプリケーション内でバイトを移動する必要がなくなり、ディスク容量を節約できます。大きなファイルの場合、これは時間とスペースを大幅に節約します。

ただし、さまざまなオペレーティングシステムでは、ハードリンクの機能に異なる制限があります。アプリケーションとターゲットシステムの構成によっては、Link()呼び出しがすべての場合に機能するとは限りません。

単一の汎用的で堅牢で効率的なコピー関数が必要な場合は、Copy()を次のように更新します。

  1. 少なくとも何らかの形式のコピーが成功することを確認するためのチェックを実行します(アクセス許可、ディレクトリが存在するなど)
  2. os.SameFileを使用して、両方のファイルがすでに存在し、同じであるかどうかを確認し、同じであれば成功を返します
  3. リンクを試み、成功したら戻る
  4. バイトをコピーし(すべての効率的な手段は失敗した)、結果を返す

最適化は、goルーチンでバイトをコピーして、呼び出し元がバイトコピーをブロックしないようにすることです。そうすることで、成功/エラーのケースを適切に処理するために呼び出し元に追加の複雑さが課​​せられます。

両方が必要な場合は、2つの異なるコピー関数があります。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'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")
    }
}
63
markc

このような関数を標準ライブラリに記述するために必要なすべてのビットがあります。これを実行するための明らかなコードを次に示します。

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

Linux/macでコードを実行している場合は、システムのcpコマンドを実行するだけで済みます。

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

それはスクリプトのように少し扱いますが、仕事は完了します。また、「os/exec」をインポートする必要があります

11
Dandalf
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)
}
9
haosdent

この場合、検証する条件がいくつかありますが、ネストされていないコードを好みます

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

Windowsを使用している場合、次のように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のラッパーに触発されています

0
js.mouret

ファイルをコピーする明白な方法は次のとおりです。

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)
    }
}
0
Salvador Dali

「exec」を使用できます。 exec.Command( "cmd"、 "/ c"、 "copy"、 "fileToBeCopied destinationDirectory")は、これを使用しており、正常に機能しています。 execの詳細については、マニュアルを参照してください。

0
jigar137