func (h *Handler) copy(ctx context.Context, newPath, oldPath string, deleteSource bool) (int, error) {
	srcKey, err := h.translateToKey(oldPath)
	if err != nil {
		return http.StatusUnprocessableEntity, errors.Wrap(err, "Invalid source filepath")
	}
	dstKey, err := h.translateToKey(newPath)
	if err != nil {
		return http.StatusUnprocessableEntity, errors.Wrap(err, "Invalid destination filepath")
	}
	if srcKey == dstKey {
		return http.StatusForbidden, nil
	}
	if err := h.Bucket.Copy(ctx, dstKey, srcKey, nil); err != nil {
		gcerr, _ := err.(interface{ Unwrap() error })
		switch e := gcerr.Unwrap().(type) {
		case *os.LinkError, *os.PathError:
			return http.StatusConflict, e
		default:
			return http.StatusInternalServerError, errors.Wrap(err, "COPY failed")
		}
	}
	if !deleteSource {
		return http.StatusCreated, nil  
	}
	if err := h.Bucket.Delete(ctx, srcKey); err != nil {
		return http.StatusInternalServerError, errors.Wrap(err, "MOVE failed")
	}
	return http.StatusCreated, nil  
}
copy is meant to respond to HTTP COPY by duplicating a file,
and MOVE if deleteSource is true.

The destination filename is parsed as if it were an URL.Path.
package upload
import (
	"context"
	"io"
	"net/http"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"github.com/pkg/errors"
	"golang.org/x/text/unicode/norm"
)
const (
	errCannotReadMIMEMultipart coreUploadError = "Error reading MIME multipart payload"
	errFileNameConflict        coreUploadError = "Name-Name Conflict"
	errInvalidFileName         coreUploadError = "Invalid filename and/or path"
	errNoDestination           coreUploadError = "A destination is missing"
	errUnknownEnvelopeFormat   coreUploadError = "Unknown envelope format"
	errLengthInvalid           coreUploadError = "Field 'length' has been set, but is invalid"
	errFileTooLarge            coreUploadError = "The uploaded file exceeds or would exceed max_filesize"
	errTransactionTooLarge     coreUploadError = "Upload(s) do or will exceed max_transaction_size"
)
type coreUploadError string
func (e coreUploadError) Error() string { return string(e) }
func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	httpCode, err := h.serveHTTP(w, r)
	if httpCode == http.StatusMethodNotAllowed && err == nil && h.Next != nil {
		h.Next.ServeHTTP(w, r)
		return
	}
	if httpCode >= 400 && err != nil {
		http.Error(w, err.Error(), httpCode)
	} else {
		w.WriteHeader(httpCode)
	}
}
func (h *Handler) serveHTTP(w http.ResponseWriter, r *http.Request) (int, error) {
	switch r.Method {
	case http.MethodPost, http.MethodPut:
	case "COPY", "MOVE", "DELETE":
		if h.EnableWebdav {  
			break
		}
		fallthrough
	default:
		return http.StatusMethodNotAllowed, nil
	}
	switch r.Method {
	case "COPY":
		destName := r.Header.Get("Destination")
		if len(r.URL.Path) < 2 || destName == "" {
			return http.StatusBadRequest, errNoDestination
		}
		return h.copy(r.Context(), destName, r.URL.Path, false)
	case "MOVE":
		destName := r.Header.Get("Destination")
		if len(r.URL.Path) < 2 || destName == "" {
			return http.StatusBadRequest, errNoDestination
		}
		return h.copy(r.Context(), destName, r.URL.Path, true)
	case "DELETE":
		if len(r.URL.Path) < 2 {
			return http.StatusBadRequest, errNoDestination
		}
		return h.deleteOneFile(r.Context(), r.URL.Path)
	case http.MethodPost:
		ctype := r.Header.Get("Content-Type")
		switch {
		case strings.HasPrefix(ctype, "multipart/form-data"):
			return h.serveMultipartUpload(w, r)
		case ctype != "":  
			return http.StatusUnsupportedMediaType, errUnknownEnvelopeFormat
		}
		fallthrough
	case http.MethodPut:
		return h.serveOneUpload(w, r)
	default:
		return http.StatusMethodNotAllowed, nil
	}
}
func (h *Handler) serveOneUpload(w http.ResponseWriter, r *http.Request) (int, error) {
	if len(r.URL.Path) < 2 {
		return http.StatusBadRequest, errNoDestination
	}
	writeQuota, overQuotaErr := h.MaxTransactionSize, errTransactionTooLarge
	if writeQuota == 0 || (h.MaxFilesize > 0 && h.MaxFilesize < writeQuota) {
		writeQuota, overQuotaErr = h.MaxFilesize, errFileTooLarge
	}
	var expectBytes int64
	if r.Header.Get("Content-Length") != "" {  
		var perr error
		expectBytes, perr = strconv.ParseInt(r.Header.Get("Content-Length"), 10, 64)
		if perr != nil || expectBytes < 0 {
			return http.StatusBadRequest, errLengthInvalid
		}
		if writeQuota > 0 && expectBytes > writeQuota {
			return http.StatusRequestEntityTooLarge, overQuotaErr  
		}
	}
	bytesWritten, key, retval, err := h.writeOneHTTPBlob(r.Context(), r.URL.Path, expectBytes, writeQuota, r.Body)
	if writeQuota > 0 && bytesWritten > writeQuota {
		return http.StatusRequestEntityTooLarge, overQuotaErr
	}
	if err == nil && h.ApparentLocation != "" {
		newApparentLocation := "/" + key
		if h.ApparentLocation != "/" {
			newApparentLocation = h.ApparentLocation + newApparentLocation
		}
		w.Header().Add("Location", newApparentLocation)
	}
	return retval, err
}
func (h *Handler) serveMultipartUpload(w http.ResponseWriter, r *http.Request) (int, error) {
	mr, err := r.MultipartReader()
	if err != nil {
		return http.StatusUnsupportedMediaType, errCannotReadMIMEMultipart
	}
	var bytesWrittenInTransaction int64
	for partNum := 1; ; partNum++ {
		part, err := mr.NextPart()
		if err == io.EOF {
			break
		}
		if err != nil {
			return http.StatusBadRequest, err
		}
		fileName := part.FileName()
		if fileName == "" {
			continue
		}
		if h.Scope == "/" {
			fileName = h.Scope + fileName
		} else {
			fileName = h.Scope + "/" + fileName
		}
		writeQuota, overQuotaErr := h.MaxFilesize, errFileTooLarge
		if h.MaxTransactionSize > 0 {
			if bytesWrittenInTransaction >= h.MaxTransactionSize {
				return http.StatusRequestEntityTooLarge, errTransactionTooLarge
			}
			if writeQuota == 0 || (h.MaxTransactionSize-bytesWrittenInTransaction) < writeQuota {
				writeQuota, overQuotaErr = h.MaxTransactionSize-bytesWrittenInTransaction, errTransactionTooLarge
			}
		}
		var expectBytes int64
		if part.Header.Get("Content-Length") != "" {
			expectBytes, err = strconv.ParseInt(part.Header.Get("Content-Length"), 10, 64)
			if err != nil || expectBytes < 0 {
				return http.StatusBadRequest, errLengthInvalid
			}
			if writeQuota > 0 && expectBytes > writeQuota {
				return http.StatusRequestEntityTooLarge, overQuotaErr
			}
		}
		bytesWritten, key, retval, err := h.writeOneHTTPBlob(r.Context(), fileName, expectBytes, writeQuota, part)
		bytesWrittenInTransaction += bytesWritten
		if writeQuota > 0 && bytesWritten > writeQuota {
			return http.StatusRequestEntityTooLarge, overQuotaErr
		}
		if err != nil {
			return retval, errors.Wrap(err, "MIME Multipart exploding failed on part "+strconv.Itoa(partNum))
		}
		if h.ApparentLocation != "" {
			newApparentLocation := "/" + key
			if h.ApparentLocation != "/" {
				newApparentLocation = h.ApparentLocation + newApparentLocation
			}
			w.Header().Add("Location", newApparentLocation)
		}
	}
	return http.StatusCreated, nil
}
func (h *Handler) translateToKey(path string) (key string, err error) {
	if path == h.Scope {
		return "", os.ErrPermission
	}
	canary := "/" + printableSuffix(15)
	key = filepath.Clean(canary + path)  
	if !strings.HasPrefix(key, canary+h.Scope) {
		err = os.ErrPermission
		return
	}
	if h.Scope == "/" {
		key = key[len(canary)+1:]
	} else {
		key = key[len(canary)+len(h.Scope)+1:]  
	}
	var enforceForm *norm.Form
	if h.UnicodeForm != nil {
		enforceForm = &h.UnicodeForm.Use
	}
	if !InAlphabet(key, h.RestrictFilenamesTo, enforceForm) {
		err = errInvalidFileName
	}
	return
}
func (h *Handler) applyRandomizedSuffix(key string) string {
	if h.RandomizedSuffixLength <= 0 {
		return key
	}
	extension := filepath.Ext(key)
	basename := strings.TrimSuffix(key, extension)
	if basename == "" || strings.HasSuffix(basename, "/") {
		key = basename + printableSuffix(h.RandomizedSuffixLength) + extension
	} else {
		key = basename + "_" + printableSuffix(h.RandomizedSuffixLength) + extension
	}
	return key
}