// 1. 并发拷贝多个照片, 分组进行并发
// 2. 有些照片的拍照日期不能获取
// 3. 指定多核后,速度明显加快
// 4. 64位的比32的又快了
package main
import (
"flag"
"fmt"
"log"
"os"
"path/filepath"
"runtime"
"strconv"
"strings"
"time"
"github.com/Unknwon/com"
"github.com/rwcarlsen/goexif/exif"
// "github.com/rwcarlsen/goexif/mknote"
)
//获取指定目录及所有子目录下的所有文件,可以匹配后缀过滤。
func WalkDir(dirPth, suffix string) (files []string, err error) {
files = make([]string, 0, 10000)
suffix = strings.ToUpper(suffix)
err = filepath.Walk(dirPth, func(filename string, fi os.FileInfo, err error) error {
if fi.IsDir() {
return nil
}
if strings.HasSuffix(strings.ToUpper(fi.Name()), suffix) {
files = append(files, filename)
}
return nil
})
return files, err
}
func Mkdir(path string) error {
err := os.MkdirAll(path, 1)
if err != nil {
return err
} else {
return nil
}
}
func GetphotoTakeDate(filename string, module string) (takendate string) {
//特别注意:如果文件描述符没有被关闭时不能rename
f, err := os.Open(filename)
defer f.Close()
if err != nil {
log.Println(err)
return "2099-01"
}
// Optionally register camera makenote data parsing - currently Nikon and
// Canon are supported.
//fmt.Println(mknote.All)
//exif.RegisterParsers(mknote.All...)
x, err := exif.Decode(f)
if err != nil {
log.Println(err)
return "2099-01"
}
// Two convenience functions exist for date/time taken and GPS coords:
if err == nil {
tm, _ := x.DateTime()
return com.DateT(tm, module)
} else {
return "2099-01"
}
}
// 如果文件总数有N,每组是M个,则返回[N1, N2, ....]
// 特别要注意最后一组
func GetchannelNum(allnum, page int) []int {
var cal int
var s []int
var div bool = (allnum%page == 0)
if allnum <= page {
return append(s, allnum)
}
if div == true {
cal = allnum / page
} else {
cal = allnum/page + 1
}
for i := 0; i <= cal-2; i++ {
s = append(s, page)
}
if div == false {
s = append(s, allnum%page)
}
fmt.Println(s)
return s
}
func ExistDirt(existdirs []string, dir string) bool {
for _, v := range existdirs {
if v == dir {
return true
}
}
return false
}
func StringsJoin(chars string, str ...string) string {
return strings.Join(str, chars)
}
func Copyphoto(srcfilename, Destdir, Yearsmodule, module, years string, done chan int) {
shortfilename := Getfilename(srcfilename)
fulldestdir := StringsJoin("\\", Destdir, years)
err := Mkdir(fulldestdir)
if err != nil {
fmt.Println("创建目录失败" + fulldestdir)
}
destfilename := StringsJoin("\\", Destdir, years, shortfilename)
if "COPY" == strings.ToUpper(module) {
err := com.Copy(srcfilename, destfilename)
if err != nil {
fmt.Printf("copying %s ---> %s error!\n", srcfilename, destfilename)
} else {
fmt.Printf("copying %s ---> %s\n", srcfilename, destfilename)
}
} else if "MOVE" == strings.ToUpper(module) {
err := os.Rename(srcfilename, destfilename)
if err != nil {
fmt.Printf("moving %s ----> %s error!\n", srcfilename, destfilename)
} else {
fmt.Printf("moving %s ----> %s\n", srcfilename, destfilename)
}
}
done <- 1
}
func Getfilename(f string) string {
fname := strings.Split(f, "\\")
return fname[len(fname)-1]
}
var Srcdir, Module, Suffix, Destdir, Yearsmodule string
var Connum int
func init() {
flag.StringVar(&Srcdir, "srcdir", "", "目录路径")
flag.IntVar(&Connum, "num", 20, "并发拷贝的个数")
flag.StringVar(&Module, "module", "copy", "copy 从源文件拷贝到目标文件夹,默认是复制\n\tmove 从源文件移到目标文件夹 ")
flag.StringVar(&Suffix, "suffix", ".jpg", "文件扩展名")
flag.StringVar(&Destdir, "destdir", "", "目标目录")
flag.StringVar(&Yearsmodule, "yearsmodule", "YYYY-MM", "YYYY-MM 按年月归类\n\tYYYY-MM-DD 按年月日归类")
flag.Usage = func() {
fmt.Fprintf(os.Stderr, "功能:%s!\n", "把指定文件夹下的照片,按日期归类")
fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])
flag.PrintDefaults()
fmt.Fprintln(os.Stderr, " -h : show help usage")
}
flag.Parse()
}
func main() {
if len(os.Args) == 1 {
fmt.Fprintln(os.Stderr, " -h : show help usage")
os.Exit(1)
}
if len(Destdir) == 0 {
fmt.Fprintln(os.Stderr, "请设置参数destdir!")
os.Exit(1)
}
if len(Srcdir) == 0 {
fmt.Fprintln(os.Stderr, "请设置参数Srcdir!")
os.Exit(1)
}
runtime.GOMAXPROCS(runtime.NumCPU())
fmt.Printf("CPU核数:%d\n", runtime.NumCPU())
start := time.Now()
//列出指定目录下所有文件
fullfilename, err := WalkDir(Srcdir, Suffix)
if err != nil {
fmt.Println(err)
return
}
// fmt.Println(fullfilename)
if len(fullfilename) == 0 {
fmt.Println("文件夹为空!")
return
}
filesnum := len(fullfilename)
page := Connum
Groups := GetchannelNum(filesnum, page)
done := make(chan int, 100)
for OutIndex := 0; OutIndex < len(Groups); OutIndex++ {
BeginEachGroup := OutIndex * page
EndEachGroup := (OutIndex + 1) * page
if OutIndex == len(Groups)-1 {
EndEachGroup = OutIndex*page + Groups[len(Groups)-1]
}
fmt.Printf("正在操作[%d, %d] \n", BeginEachGroup, EndEachGroup)
for _, v := range fullfilename[BeginEachGroup:EndEachGroup] {
go Copyphoto(v, Destdir, Yearsmodule, Module, GetphotoTakeDate(v, Yearsmodule), done)
}
for i := BeginEachGroup; i < EndEachGroup; i++ {
<-done
}
}
close(done)
t := time.Now()
elapsed := t.Sub(start)
fmt.Println("finish ^^!")
fmt.Println("文件总数:" + strconv.Itoa(filesnum))
fmt.Printf("耗时=%v\n", elapsed)
}