go语言 os.Rename() cannot move the file to a different disk drive 怎么办

时值我小病在家休养生息,喜欢跳广场舞的外公来寻求我的帮助,他们跳广场舞是将存有歌曲的U盘插到音响上面,而音响大部分都是只能显示歌曲的索引index,不能直接显示歌曲名,所以为了方便他们会在U盘里面对歌曲进行排序。由于音响是寻址按顺序播放,意思就是在U盘里面的歌曲需要一首一首的按顺序复制过去,而且当对U盘歌曲进行增添的时候又需要按照顺序重新复制一遍,可以说相当麻烦。为了将我从这重复的劳动中解放出来,我用go语言写了一个小工具,本来想着分分钟写完,却没想到踩到了坑。
os包中有一个Rename()函数具有重命名和移动的功能,其函数如下:

func Rename(oldpath, newpath string) error

Rename修改一个文件的名字,移动一个文件。可能会有一些个操作系统特定的限制。

在windows系统下面使用该函数,oldpathnewpath在同一个磁盘/卷下面能正常使用,可我需要将音乐移动到U盘上,当使用这个函数的时候出现了:

The system cannot move the file to a different disk drive.

搜索Githubissues,发现Rename在windows中不能进行跨磁盘/卷操作。
为了实现跨磁盘/卷操作,一种方法是直接调用windows API,于是在windows api docs中搜索到movefileex()函数能够实现该功能,但是在go语言的syscall包中只有movefile()函数,其函数如下:

func MoveFile(from *uint16, to *uint16) (err error)

实现文件移动的函数可以写成:

func movefile(oldpath, newpath string) error { //跨卷移动
	from, err := syscall.UTF16PtrFromString(oldpath)
	if err != nil {
		return err
	}
	to, err := syscall.UTF16PtrFromString(newpath)
	if err != nil {
		return err
	}
	return syscall.MoveFile(from, to)//windows API

}

当我们读取oldpath目录里面的文件时调用io/ioutil包的ReadDir()函数可按照文件夹内的排序方式批量读入文件名,其函数实现如下:

func filenamelist(filepath string) []string {
	var list []string
	rd, err := ioutil.ReadDir(filepath) //遍历目录
	if err != nil {
		log.Fatal(err)
	}
	for _, fi := range rd {
		if fi.IsDir() {
			fmt.Printf("[%s]is dir\n", fi.Name())

		} else {
			list = append(list, fi.Name())
			fmt.Println(filepath + fi.Name())
		}
	}
	return list//返回一个string数组

}

当然也可以普通方式读入文件名然后用sort包进行排序。
下面是完整代码:

package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"syscall"
)

func movefile(oldpath, newpath string) error { //跨卷移动
	from, err := syscall.UTF16PtrFromString(oldpath)
	if err != nil {
		return err
	}
	to, err := syscall.UTF16PtrFromString(newpath)
	if err != nil {
		return err
	}
	return syscall.MoveFile(from, to)//windows API

}
func filenamelist(filepath string) []string {
	var list []string
	rd, err := ioutil.ReadDir(filepath) //遍历目录
	if err != nil {
		log.Fatal(err)
	}
	for _, fi := range rd {
		if fi.IsDir() {
			fmt.Printf("[%s]is dir\n", fi.Name())

		} else {
			list = append(list, fi.Name())
			fmt.Println(filepath + fi.Name())
		}
	}
	return list

}
func movefilelist(oldpath, newpath string) {
	for _, fi := range filenamelist(oldpath) { //移动目录的所有文件
		err := movefile(oldpath+fi, newpath+fi)
		if err != nil {
			log.Fatal(err)
		}
		fmt.Println(fi + "--Move To -->" + newpath + "--OK!")

	}
}
func main() {
	movefilelist(os.Args[1], os.Args[2])
}

最后在终端中:

Ryan@DESKTO MINGW64 /Go/test (master)
$ go run main.go ~/Desktop/music/ /d/
C:/Users/Ryan/Desktop/music/新建文本文档 (2).txt
C:/Users/Ryan/Desktop/music/新建文本文档.txt
新建文本文档 (2).txt--Move To -->D:/--OK!
新建文本文档.txt--Move To -->D:/--OK!

有了这个脚本,我今后终于可以愉快的帮助我外公了。

转载请注明

posted @ 2018-11-28 14:22  Ryan_M  阅读(612)  评论(0编辑  收藏  举报