package main import ( "biukop.com/sfm/loan" "crypto/sha256" "errors" "fmt" log "github.com/sirupsen/logrus" "io" "io/ioutil" "net/http" "os" "path/filepath" "strconv" "time" ) func apiV1UploadsGet(w http.ResponseWriter, r *http.Request, ss *loan.Session) { id := r.URL.Path[len(apiV1Prefix+"lender-upload/"):] //remove prefix intId, e := strconv.Atoi(id) if e != nil { log.Println("invalid id for upload get", id, e) apiV1Client403Error(w, r, ss) // bad request return } ul := loan.Uploads{} e = ul.Read(int64(intId)) if e != nil { log.Println("no file uploaded", intId, e) apiV1Client404Error(w, r, ss) // bad request return } //check local file first path := config.Uploads + strconv.FormatInt(ul.Id, 10) + ".uploads" if fileExists(path) { http.ServeFile(w, r, path) return } log.Error("Upload file not found on disk", ul) apiV1Server500Error(w, r) // bad request } func apiV1UploadsPost(w http.ResponseWriter, r *http.Request, ss *loan.Session) { id := r.URL.Path[len(apiV1Prefix+"lender-upload/"):] //remove prefix filename, e := saveUploadToFile(r) if e != nil { log.Println("no file uploaded", filename, e) apiV1Client404Error(w, r, ss) // bad request return } intId, e := strconv.Atoi(id) if id != "" { if e != nil { log.Println("Error Getting File", e) apiV1Client404Error(w, r, ss) // bad request return } updateUploads(int64(intId), filename, w, r, ss) } else { createUploads(filename, w, r, ss) } } func sha256File(input io.Reader) string { hash := sha256.New() if _, err := io.Copy(hash, input); err != nil { log.Fatal(err) } sum := hash.Sum(nil) return fmt.Sprintf("%x", sum) } func updateUploads(id int64, fileName string, w http.ResponseWriter, r *http.Request, ss *loan.Session) { ul := loan.Uploads{} e := ul.Read(int64(id)) if e != nil { log.Println("bad upload id is given ", id, e) apiV1Client404Error(w, r, ss) // bad request return } ul1, _, e := saveUploadsToDB(id, fileName, r, ss) if e != nil { os.Remove(config.Uploads + ul.FileName) ul1.Delete() log.Println("cannot save file info to db ", e) apiV1Server500Error(w, r) // bad request return } apiV1SendJson(ul1, w, r, ss) } func createUploads(fileName string, w http.ResponseWriter, r *http.Request, ss *loan.Session) { ul, _, e := saveUploadsToDB(0, fileName, r, ss) if e != nil { log.Println("cannot save file info to db ", e) e = ul.Delete() // delete the newly created, if failed, db will clean it if e != nil { log.Error("failed to remove unused uploads", ul) } e = os.Remove(config.Uploads + fileName) if e != nil { log.Error("failed to remove unused temp file", fileName) } apiV1Server500Error(w, r) // bad request return } apiV1SendJson(ul, w, r, ss) } func saveUploadsToDB(id int64, fileName string, r *http.Request, ss *loan.Session) (ul loan.Uploads, duplicate bool, e error) { duplicate = false e = r.ParseMultipartForm(10 << 20) // we should have ready parsed this, just in case if e != nil { return } file, header, e := r.FormFile("files") file.Seek(0, 0) //seek to beginning checksum := sha256File(file) ul.Id = id ul.Ts = time.Now() ul.FileName = header.Filename file.Seek(0, 0) //seek to beginning ul.Format = header.Header.Get("Content-type") ul.Size = header.Size // necessary to prevent duplicate ul.LastModified = 0 ul.Sha256 = checksum // necessary to prevent duplicate ul.By = ss.User e = ul.Write() // this Id will have the real Id if there is duplicates if e != nil { log.Error("Fail to update db ", ul, e) } else { if id > 0 && ul.Id != id { duplicate = true } target := fmt.Sprintf("%d.uploads", ul.Id) e = os.Rename(config.Uploads+fileName, config.Uploads+target) if e != nil { ul.FileName = fileName // some how failed to rename } } return } func saveUploadToFile(r *http.Request) (filename string, e error) { e = r.ParseMultipartForm(10 << 20) if e != nil { return } file, header, e := r.FormFile("files") if e != nil { log.Println("Error Getting File", e) return } out, pathError := ioutil.TempFile(config.Uploads, "can-del-upload-*.tmp") if pathError != nil { log.Println("Error Creating a file for writing", pathError) return } out.Seek(0, 0) //seek to beginning size, e := io.Copy(out, file) if e != nil { os.Remove(out.Name()) //remove on failure log.Println("Error copying", e) return } if size != header.Size { e = errors.New("written file with incorrect size") } return filepath.Base(out.Name()), e }