【问题标题】:Move a file to a different drive with Go使用 Go 将文件移动到其他驱动器
【发布时间】:2022-04-24 19:26:51
【问题描述】:

我正在尝试使用 os.Replace() 将文件从 C 盘移动到 H 盘。

代码如下:

func MoveFile(source string, destination string) {
    err := os.Rename(source, destination)
    if err != nil {
        fmt.Println(err)
    }
}

但是,当我运行代码时,出现以下错误:

rename C:\old\path\to\file.txt H:\new\path\to\file.txt: The system cannot move the file to a different disk drive.

我在 GitHub 上发现了this 问题,指出了问题,但似乎他们不会更改此功能以允许它在不同的磁盘驱动器上移动文件。

我已经搜索了移动文件的其他可能性,但在标准文档或互联网上一无所获。

那么,我现在应该怎么做才能在不同的磁盘驱动器上移动文件?

【问题讨论】:

  • 嗯,这应该很明显:您将文件复制到新位置(创建一个新文件)并在副本成功写入后删除旧文件。
  • 好的,但是 go 似乎没有提供复制文件的方法,我在 os 包中找不到任何东西。还是我需要创建自己的复制功能,在其中创建一个全新的文件并使用流或复制数据?
  • 是的,对于小文件来说这完全是微不足道的(比如 2 行),对于大文件来说只是稍微复杂一些。

标签: go


【解决方案1】:

正如评论所说,您需要在另一个磁盘上创建一个新文件,复制内容,然后删除原始文件。使用os.Createio.Copyos.Remove 很简单:

import (
    "fmt"
    "io"
    "os"
)

func MoveFile(sourcePath, destPath string) error {
    inputFile, err := os.Open(sourcePath)
    if err != nil {
        return fmt.Errorf("Couldn't open source file: %s", err)
    }
    outputFile, err := os.Create(destPath)
    if err != nil {
        inputFile.Close()
        return fmt.Errorf("Couldn't open dest file: %s", err)
    }
    defer outputFile.Close()
    _, err = io.Copy(outputFile, inputFile)
    inputFile.Close()
    if err != nil {
        return fmt.Errorf("Writing to output file failed: %s", err)
    }
    // The copy was successful, so now delete the original file
    err = os.Remove(sourcePath)
    if err != nil {
        return fmt.Errorf("Failed removing original file: %s", err)
    }
    return nil
}

【讨论】:

  • 谢谢,根据@volker 的评论,我使用ioutil.ReadFileioutil.WriteFileos.Remove 创建了一个类似的函数,这些也很好,还是你的版本更好?
  • 使用ioutil.ReadFile 通常适用于较小的文件,但它确实会将整个文件复制到一个切片中。这意味着如果您使用这些功能,“移动”一个大文件将占用大量内存。我使用io.Copy 发布的版本不需要一次将整个文件读入内存,并且应该更适合大文件并降低内存使用率。如果你不关心大文件,那么就做任何更简单的事情,但一般来说,我建议使用io.Copy ——它适用于像你这样的情况,在这种情况下你不需要在写入之前修改数据它。
  • io.Copy 甚至可以利用内核特定的系统调用来完全避免将文件加载到用户空间。你应该使用io.Copy
  • @Vhitewidow:ioutil.ReadFile`一次将整个文件读入内存。它只适用于小填充物。
  • 不涉及所有权或权限
【解决方案2】:

您需要确保在 Linux 和 Windows 上处理所有情况。例如,对于任何大小的文件,

package main

import (
    "flag"
    "fmt"
    "io"
    "os"
)

func MoveFile(source, destination string) (err error) {
    src, err := os.Open(source)
    if err != nil {
        return err
    }
    defer src.Close()
    fi, err := src.Stat()
    if err != nil {
        return err
    }
    flag := os.O_WRONLY | os.O_CREATE | os.O_TRUNC
    perm := fi.Mode() & os.ModePerm
    dst, err := os.OpenFile(destination, flag, perm)
    if err != nil {
        return err
    }
    defer dst.Close()
    _, err = io.Copy(dst, src)
    if err != nil {
        dst.Close()
        os.Remove(destination)
        return err
    }
    err = dst.Close()
    if err != nil {
        return err
    }
    err = src.Close()
    if err != nil {
        return err
    }
    err = os.Remove(source)
    if err != nil {
        return err
    }
    return nil
}

func main() {
    var src, dst string
    flag.StringVar(&src, "src", "", "source file")
    flag.StringVar(&dst, "dst", "", "destination file")
    flag.Parse()
    if src == "" || dst == "" {
        flag.Usage()
        os.Exit(1)
    }

    err := MoveFile(src, dst)
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
        os.Exit(1)
    }
    fmt.Printf("moved %q to %q\n", src, dst)
}

输出(Linux):

$ cp move.file src.file && go build movefile.go && ./movefile -src=src.file -dst=dst.file
moved "src.file" to "dst.file"
$

输出(Windows):

>copy /Y move.file src.file && go build movefile.go && movefile -src=src.file -dst=dst.file
moved "src.file" to "dst.file"
>

【讨论】:

    【解决方案3】:

    此解决方案移动文件并保留权限:

    func MoveFile(src, dst string) error {
        in, err := os.Open(src)
        if err != nil {
            return fmt.Errorf("Couldn't open source file: %s", err)
        }
    
        out, err := os.Create(dst)
        if err != nil {
            in.Close()
            return fmt.Errorf("Couldn't open dest file: %s", err)
        }
        defer out.Close()
    
        _, err = io.Copy(out, in)
        in.Close()
        if err != nil {
            return fmt.Errorf("Writing to output file failed: %s", err)
        }
    
        err = out.Sync()
        if err != nil {
            return fmt.Errorf("Sync error: %s", err)
        }
    
        si, err := os.Stat(src)
        if err != nil {
            return fmt.Errorf("Stat error: %s", err)
        }
        err = os.Chmod(dst, si.Mode())
        if err != nil {
            return fmt.Errorf("Chmod error: %s", err)
        }
    
        err = os.Remove(src)
        if err != nil {
            return fmt.Errorf("Failed removing original file: %s", err)
        }
        return nil
    }
    

    如果只想复制文件而不删除原始文件:

    func CopyFile(src, dst string) error {
        in, err := os.Open(src)
        if err != nil {
            return fmt.Errorf("Couldn't open source file: %s", err)
        }
    
        out, err := os.Create(dst)
        if err != nil {
            in.Close()
            return fmt.Errorf("Couldn't open dest file: %s", err)
        }
        defer out.Close()
    
        _, err = io.Copy(out, in)
        in.Close()
        if err != nil {
            return fmt.Errorf("Writing to output file failed: %s", err)
        }
    
        err = out.Sync()
        if err != nil {
            return fmt.Errorf("Sync error: %s", err)
        }
    
        si, err := os.Stat(src)
        if err != nil {
            return fmt.Errorf("Stat error: %s", err)
        }
        err = os.Chmod(dst, si.Mode())
        if err != nil {
            return fmt.Errorf("Chmod error: %s", err)
        }
    
        return nil
    }
    

    【讨论】:

      【解决方案4】:

      也许您可以使用一种神奇的方法,只需使用syscall.MoveFile,如下所示。

      func main() {
          oldpath := "D:\\black.txt"
          newpath := "E:\\black-new.txt"
      
          from, _ := syscall.UTF16PtrFromString(oldpath)
          to, _ := syscall.UTF16PtrFromString(newpath)
          fmt.Println(*from, *to)
          err := syscall.MoveFile(from, to)
          if err != nil {
              panic(err)
          }
      }
      

      程序有效。

      如果你想要一个跨平台兼容的程序,你可以实现自己的MoveFile

      func MoveFile(src string, dst string) error {
          if runtime.GOOS == "windows" {
              from, _ := syscall.UTF16PtrFromString(src)
              to, _ := syscall.UTF16PtrFromString(dst)
              return syscall.MoveFile(from, to)
          } else {
              return os.Rename(src, dst)
          }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-04-08
        • 1970-01-01
        • 2017-10-09
        • 1970-01-01
        • 2015-03-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多