Add support for copying a file

This commit is contained in:
Dane Everitt
2019-05-04 17:02:01 -07:00
parent 4318d9988a
commit 50d16a3dcd
2 changed files with 101 additions and 0 deletions

View File

@@ -4,6 +4,7 @@ import (
"bytes"
"encoding/json"
"errors"
"fmt"
"github.com/gabriel-vasile/mimetype"
"go.uber.org/zap"
"io"
@@ -12,6 +13,7 @@ import (
"path"
"path/filepath"
"sort"
"strconv"
"strings"
"sync"
"time"
@@ -287,6 +289,87 @@ func (fs *Filesystem) Rename(from string, to string) error {
return os.Rename(cleanedFrom, cleanedTo)
}
// Copies a given file to the same location and appends a suffix to the file to indicate that
// it has been copied.
//
// @todo need to get an exclusive lock on the file.
func (fs *Filesystem) Copy(p string) error {
cleaned, err := fs.SafePath(p)
if err != nil {
return err
}
if s, err := os.Stat(cleaned); (err != nil && os.IsNotExist(err)) || s.IsDir() || !s.Mode().IsRegular() {
// For now I think I am okay just returning a nil response if the thing
// we're trying to copy doesn't exist. Probably will want to come back and
// re-evaluate if this is a smart decision (I'm guessing not).
return nil
} else if err != nil {
return err
}
base := filepath.Base(cleaned)
relative := strings.TrimSuffix(strings.TrimPrefix(cleaned, fs.Path()), base)
extension := filepath.Ext(base)
name := strings.TrimSuffix(base, filepath.Ext(base))
// Begin looping up to 50 times to try and create a unique copy file name. This will take
// an input of "file.txt" and generate "file copy.txt". If that name is already taken, it will
// then try to write "file copy 2.txt" and so on, until reaching 50 loops. At that point we
// won't waste anymore time, just use the current timestamp and make that copy.
//
// Could probably make this more efficient by checking if there are any files matching the copy
// pattern, and trying to find the highest number and then incrementing it by one rather than
// looping endlessly.
var i int
copySuffix := " copy"
for i = 0; i < 51; i++ {
if i > 0 {
copySuffix = " copy " + strconv.Itoa(i)
}
tryName := fmt.Sprintf("%s%s%s", name, copySuffix, extension)
tryLocation, err := fs.SafePath(path.Join(relative, tryName))
if err != nil {
return err
}
// If the file exists, continue to the next loop, otherwise we're good to start a copy.
if _, err := os.Stat(tryLocation); err != nil && !os.IsNotExist(err) {
return err
} else if os.IsNotExist(err) {
break
}
if i == 50 {
copySuffix = "." + time.Now().Format(time.RFC3339)
}
}
finalPath, err := fs.SafePath(path.Join(relative, fmt.Sprintf("%s%s%s", name, copySuffix, extension)))
if err != nil {
return err
}
source, err := os.Open(cleaned)
if err != nil {
return err
}
defer source.Close()
dest, err := os.Create(finalPath)
if err != nil {
return err
}
defer dest.Close()
if _, err := io.Copy(dest, source); err != nil {
return err
}
return nil
}
// Lists the contents of a given directory and returns stat information about each
// file and folder within it.
func (fs *Filesystem) ListDirectory(p string) ([]*Stat, error) {