github.com/kotovmak/go-admin@v1.1.1/modules/file/file.go (about) 1 // Copyright 2019 GoAdmin Core Team. All rights reserved. 2 // Use of this source code is governed by a Apache-2.0 style 3 // license that can be found in the LICENSE file. 4 5 package file 6 7 import ( 8 "fmt" 9 "io" 10 "mime/multipart" 11 "os" 12 "path" 13 "sync" 14 15 "github.com/kotovmak/go-admin/plugins/admin/modules" 16 ) 17 18 // Uploader is a file uploader which contains the method Upload. 19 type Uploader interface { 20 Upload(*multipart.Form) error 21 } 22 23 // UploaderGenerator is a function return an Uploader. 24 type UploaderGenerator func() Uploader 25 26 var uploaderList = map[string]UploaderGenerator{ 27 "local": GetLocalFileUploader, 28 } 29 30 var upMu sync.Mutex 31 32 // AddUploader makes a uploader generator available by the provided theme name. 33 // If Add is called twice with the same name or if uploader is nil, 34 // it panics. 35 func AddUploader(name string, up UploaderGenerator) { 36 upMu.Lock() 37 defer upMu.Unlock() 38 if up == nil { 39 panic("uploader generator is nil") 40 } 41 if _, dup := uploaderList[name]; dup { 42 panic("add uploader generator twice " + name) 43 } 44 uploaderList[name] = up 45 } 46 47 // GetFileEngine return the Uploader of given name. 48 func GetFileEngine(name string) Uploader { 49 if up, ok := uploaderList[name]; ok { 50 return up() 51 } 52 panic("wrong uploader name") 53 } 54 55 // UploadFun is a function to process the uploading logic. 56 type UploadFun func(*multipart.FileHeader, string) (string, error) 57 58 // Upload receive the return value of given UploadFun and put them into the form. 59 func Upload(c UploadFun, form *multipart.Form) error { 60 var ( 61 suffix string 62 filename string 63 ) 64 65 for k := range form.File { 66 for _, fileObj := range form.File[k] { 67 suffix = path.Ext(fileObj.Filename) 68 filename = modules.Uuid() + suffix 69 70 pathStr, err := c(fileObj, filename) 71 72 if err != nil { 73 return err 74 } 75 76 form.Value[k] = append(form.Value[k], pathStr) 77 form.Value[k+"_size"] = append(form.Value[k+"_size"], fmt.Sprintf("%d", fileObj.Size)) 78 } 79 } 80 81 return nil 82 } 83 84 // SaveMultipartFile used in a local Uploader which help to save file in the local path. 85 func SaveMultipartFile(fh *multipart.FileHeader, path string) error { 86 f, err := fh.Open() 87 if err != nil { 88 return err 89 } 90 91 if ff, ok := f.(*os.File); ok { 92 // Windows can't rename files that are opened. 93 if err := f.Close(); err != nil { 94 return err 95 } 96 97 // If renaming fails we try the normal copying method. 98 // Renaming could fail if the files are on different devices. 99 if os.Rename(ff.Name(), path) == nil { 100 return nil 101 } 102 103 // Reopen f for the code below. 104 f, err = fh.Open() 105 if err != nil { 106 return err 107 } 108 } 109 110 defer func() { 111 if err2 := f.Close(); err2 != nil { 112 err = err2 113 } 114 }() 115 116 ff, err := os.Create(path) 117 if err != nil { 118 return err 119 } 120 121 defer func() { 122 if err2 := ff.Close(); err2 != nil { 123 err = err2 124 } 125 }() 126 _, err = copyZeroAlloc(ff, f) 127 return err 128 } 129 130 func copyZeroAlloc(w io.Writer, r io.Reader) (int64, error) { 131 buf := copyBufPool.Get().([]byte) 132 n, err := io.CopyBuffer(w, r, buf) 133 copyBufPool.Put(buf) 134 return n, err 135 } 136 137 var copyBufPool = sync.Pool{ 138 New: func() interface{} { 139 return make([]byte, 4096) 140 }, 141 }