func (be *Backend) UploadFile(bin string, filename string, data io.ReadCloser) (File, error) { be.Log.Println("Uploading file " + filename + " to bin " + bin) f := File{} f.Filename = filename f.Bin = bin if !isDir(be.tempdir) { if err := os.Mkdir(be.tempdir, 0700); err != nil { return f, err } } fp, err := ioutil.TempFile(be.tempdir, "upload") // Defer removal of the tempfile to clean up partially uploaded files. defer os.Remove(fp.Name()) defer fp.Close() if err != nil { be.Log.Println(err) return f, err } bytes, err := io.Copy(fp, data) if err != nil { be.Log.Println("Error occurred during io.Copy: " + err.Error()) return f, err } be.Log.Println("Uploaded " + strconv.FormatInt(bytes, 10) + " bytes") f.Bytes = bytes if bytes == 0 { be.Log.Println("Empty files are not allowed. Aborting.") if err := os.Remove(fp.Name()); err != nil { be.Log.Println(err) return f, err } err := errors.New("No content. The file size must be more than 0 bytes") return f, err } buffer := make([]byte, 512) _, err = fp.Seek(0, 0) if err != nil { return f, err } _, err = fp.Read(buffer) if err != nil { return f, err } f.MIME = http.DetectContentType(buffer) hash := sha256.New() fp.Seek(0, 0) if err != nil { return f, err } _, err = io.Copy(hash, fp) if err != nil { return f, err } var result []byte f.Checksum = hex.EncodeToString(hash.Sum(result)) // Exif if strings.Split(f.MIME, "/")[0] == "image" { if _, err = fp.Seek(0, 0); err != nil { return f, err } exif, err := exif.Decode(fp) if err != nil { // XXX: Log } else { f.DateTime, err = exif.DateTime() if err != nil { // Could not read DateTime or DateTimeOriginal. // Certain Android phones (verified on Nexus 6) have a // bug where these fields may be missing from the EXIF // data in pictures. The capture time may be stored in // the GPS info, so let's try to assemble a valid // DateTime from it. dStamp, dErr := exif.Get("GPSDateStamp") tStamp, tErr := exif.Get("GPSTimeStamp") if dErr == nil && tErr == nil { s := dStamp.String() + " " + tStamp.String() s = strings.Replace(s, "\"", "", -1) s = strings.Replace(s, "[", "", -1) s = strings.Replace(s, "]", "", -1) s = strings.Replace(s, ":", "-", -1) s = strings.Replace(s, ",", " ", -1) s = strings.Replace(s, "/1", "", -1) be.Log.Println("String to parse: " + s) // After removing the special characters above, the // string to parse is on the following format: // 2016-05-21 12 26 12 dt, err := time.Parse("2006-01-02 15 4 5", s) if err != nil { be.Log.Println("Unable to parse: ", err) } else { f.DateTime = dt.Local() } } } f.Latitude, f.Longitude, err = exif.LatLong() if err != nil { /// XXX: Log } } } // iOS devices provide only one filename even when uploading // multiple images. Providing some workaround for this below. // XXX: Refactoring needed. Can probably be done nicer. if strings.Split(f.MIME, "/")[0] == "image" && !f.DateTime.IsZero() { dt := f.DateTime.Format("060102-150405") var fname string // List of filenames to modify if filename == "image.jpeg" { fname = "img-" + dt + ".jpeg" } if filename == "image.gif" { fname = "img-" + dt + ".gif" } if filename == "image.png" { fname = "img-" + dt + ".png" } if fname != "" { be.Log.Println("Filename workaround triggered") be.Log.Println("Filename modified: " + fname) f.Filename = fname } } bindir := filepath.Join(be.filedir, bin) if !isDir(bindir) { if err := os.Mkdir(bindir, 0700); err != nil { return f, err } } dst := filepath.Join(bindir, f.Filename) be.Log.Println("Copying contents to " + dst) if err := CopyFile(fp.Name(), dst); err != nil { be.Log.Println(err) return f, err } fi, err := os.Lstat(dst) if err != nil { be.Log.Println(err) return f, err } f.CreatedAt = fi.ModTime() f.Links = be.GenerateLinks(bin, f.Filename) be.Lock() defer be.Unlock() id := f.Bin + f.Filename delete(be.files, id) be.files[id] = f return f, err }
func (be *Backend) getFileMetaData(bin string, filename string) (File, error) { be.Log.Println("Reading file meta data: " + filename + " (" + bin + ")...") f := File{} path := filepath.Join(be.filedir, bin, filename) // File info fi, err := os.Lstat(path) if err != nil || fi.IsDir() == true { return f, errors.New("File does not exist.") } f.Bin = bin f.Filename = filename f.Bytes = fi.Size() f.CreatedAt = fi.ModTime() // Calculate checksum fp, err := os.Open(path) if err != nil { return f, err } defer fp.Close() hash := sha256.New() if _, err = io.Copy(hash, fp); err != nil { return f, err } var result []byte f.Checksum = hex.EncodeToString(hash.Sum(result)) // MIME buffer := make([]byte, 512) if _, err = fp.Seek(0, 0); err != nil { return f, err } if _, err = fp.Read(buffer); err != nil { return f, err } f.MIME = http.DetectContentType(buffer) f.Links = be.GenerateLinks(bin, filename) // Exif if strings.Split(f.MIME, "/")[0] == "image" { if _, err = fp.Seek(0, 0); err != nil { return f, err } exif, err := exif.Decode(fp) if err != nil { // XXX: Log } else { f.DateTime, err = exif.DateTime() if err != nil { // Could not read DateTime or DateTimeOriginal. // Certain Android phones (verified on Nexus 6) have a // bug where these fields may be missing from the EXIF // data in pictures. The capture time may be stored in // the GPS info, so let's try to assemble a valid // DateTime from it. dStamp, dErr := exif.Get("GPSDateStamp") tStamp, tErr := exif.Get("GPSTimeStamp") if dErr == nil && tErr == nil { s := dStamp.String() + " " + tStamp.String() s = strings.Replace(s, "\"", "", -1) s = strings.Replace(s, "[", "", -1) s = strings.Replace(s, "]", "", -1) s = strings.Replace(s, ":", "-", -1) s = strings.Replace(s, ",", " ", -1) s = strings.Replace(s, "/1", "", -1) be.Log.Println("String to parse: " + s) // After removing the special characters above, the // string to parse is on the following format: // 2016-05-21 12 26 12 dt, err := time.Parse("2006-01-02 15 4 5", s) if err != nil { be.Log.Println("Unable to parse: ", err) } else { f.DateTime = dt.Local() } } } f.Latitude, f.Longitude, err = exif.LatLong() if err != nil { /// XXX: Log } } } return f, nil }