func (fs Fs) Writer(f kit.File, create bool) (string, io.WriteCloser, apperror.Error) { id := f.GetBackendId() if create { id = f.GetFullName() } return fs.WriterById(f.GetBucket(), id, create) }
func (r *FilesResource) getImageReader(registry kit.Registry, tmpDir string, file kit.File, width, height int64, filters []string, ip string) (reader kit.ReadSeekerCloser, size int64, err apperror.Error) { if width == 0 && height == 0 && len(filters) == 0 { reader, err = file.Reader() return } // Dimensions specified. // Check if the thumbnail was already created. // If so, serve it. Otherwise, create it first. if (width == 0 || height == 0) && (file.GetWidth() == 0 || file.GetHeight() == 0) { err = &apperror.Err{ Code: "image_dimensions_not_determined", Message: fmt.Sprintf("The file with id %v does not have width/height", file.GetId()), } return } if width < 0 || height < 0 { err = apperror.New("invalid_dimensions") return } // If either height or width is 0, determine proper values to presserve aspect ratio. if width == 0 { ratio := float64(file.GetWidth()) / float64(file.GetHeight()) width = int64(float64(height) * ratio) } else if height == 0 { ratio := float64(file.GetHeight()) / float64(file.GetWidth()) height = int64(float64(width) * ratio) } maxWidth := registry.Config().UInt("files.thumbGenerator.maxWidth", 2000) maxHeight := registry.Config().UInt("files.thumbGenerator.maxHeight", 2000) if width > int64(maxWidth) || height > int64(maxHeight) { err = &apperror.Err{ Code: "dimensions_exceed_maximum_limits", Message: "The specified dimensions exceed the maximum limits", } return } thumbId := fmt.Sprintf("%v_%v_%v_%v_%v_%v.%v", file.GetId(), file.GetBucket(), file.GetName(), strconv.FormatInt(width, 10), strconv.FormatInt(height, 10), strings.Replace(strings.Join(filters, "_"), ":", "_", -1), "jpeg") if ok, _ := file.GetBackend().HasFileById("thumbs", thumbId); !ok { var channel chan bool channel, err = r.thumbnailRateLimiter.Start(ip) if err != nil { return } if channel != nil { <-channel } // Thumb does not exist yet, so create it. reader, err = file.Reader() if err != nil { return } defer reader.Close() img, _, err2 := image.Decode(reader) if err2 != nil { err = apperror.Wrap(err2, "image_decode_error") return } var giftFilters []gift.Filter if !(height == 0 && width == 0) { giftFilters = append(giftFilters, gift.ResizeToFill(int(width), int(height), gift.LanczosResampling, gift.CenterAnchor)) } for _, filter := range filters { if filter == "" { continue } parts := strings.Split(filter, ":") if len(parts) > 1 { filter = parts[0] } switch filter { case "sepia": n := float32(100) if len(parts) == 2 { x, err2 := strconv.ParseFloat(parts[1], 64) if err2 == nil { n = float32(x) } else { err = apperror.New("invalid_sepia_filter_value", true) return } } giftFilters = append(giftFilters, gift.Sepia(n)) case "grayscale": giftFilters = append(giftFilters, gift.Grayscale()) case "brightness": n := float32(0) if len(parts) == 2 { x, err2 := strconv.ParseFloat(parts[1], 64) if err2 == nil { n = float32(x) } else { err = apperror.New("invalid_brightness_filter_value", true) return } } giftFilters = append(giftFilters, gift.Brightness(n)) default: err = apperror.New("unknown_filter", fmt.Sprintf("Unknown filter: %v", filter), true) return } } gift := gift.New(giftFilters...) thumb := image.NewRGBA(gift.Bounds(img.Bounds())) gift.Draw(thumb, img) var writer io.WriteCloser _, writer, err = file.GetBackend().WriterById("thumbs", thumbId, true) if err != nil { return } defer writer.Close() jpeg.Encode(writer, thumb, &jpeg.Options{Quality: 90}) r.thumbnailRateLimiter.Finish() } backend := file.GetBackend() size, err = backend.FileSizeById("thumbs", thumbId) if err != nil { return } reader, err = file.GetBackend().ReaderById("thumbs", thumbId) return }
func (fs Fs) Reader(f kit.File) (kit.ReadSeekerCloser, apperror.Error) { return fs.ReaderById(f.GetBucket(), f.GetBackendId()) }
func (fs Fs) DeleteFile(f kit.File) apperror.Error { return fs.DeleteFileById(f.GetBucket(), f.GetBackendId()) }
func (fs Fs) FileSize(file kit.File) (int64, apperror.Error) { return fs.FileSizeById(file.GetBucket(), file.GetBackendId()) }
func (fs Fs) HasFile(f kit.File) (bool, apperror.Error) { return fs.HasFileById(f.GetBucket(), f.GetBackendId()) }
func (h FileService) BuildFile(file kit.File, user kit.User, deleteDir, deleteFile bool) apperror.Error { if h.DefaultBackend == nil { return &apperror.Err{ Code: "no_default_backend", Message: "Cant build a file without a default backend.", } } filePath := file.GetTmpPath() if filePath == "" { return &apperror.Err{ Code: "no_tmp_path", Message: "You must set TmpPath on a file before building it.", Public: true, } } if file.GetBackendName() == "" { file.SetBackendName(h.DefaultBackend().Name()) } backend := h.Backend(file.GetBackendName()) if backend == nil { return &apperror.Err{ Code: "unknown_backend", Message: fmt.Sprintf("The backend %v does not exist", file.GetBackendName()), } } file.SetBackend(backend) if file.GetBucket() == "" { return &apperror.Err{ Code: "missing_bucket", Message: "Bucket must be set on the file", } } stat, err := os.Stat(filePath) if err != nil { if err == os.ErrNotExist { return &apperror.Err{ Code: "file_not_found", Message: fmt.Sprintf("File %v does not exist", filePath), } } return apperror.Wrap(err, "stat_error", fmt.Sprintf("Could not get file stats for file at %v: %v", filePath, err)) } if stat.IsDir() { return apperror.New("path_is_directory") } // Build the hash. hash, err2 := utils.BuildFileMD5Hash(filePath) if err2 != nil { return err2 } file.SetHash(hash) pathParts := strings.Split(filePath, string(os.PathSeparator)) fullName := pathParts[len(pathParts)-1] nameParts := strings.Split(fullName, ".") // Determine extension. extension := "" if len(nameParts) > 1 { extension = nameParts[len(nameParts)-1] } file.SetFullName(fullName) file.SetSize(stat.Size()) // Determine mime type. mimeType := GetMimeType(filePath) if mimeType == "" { mimeType = mime.TypeByExtension("." + extension) } file.SetMime(mimeType) if strings.HasPrefix(mimeType, "image") { file.SetIsImage(true) file.SetMediaType(MEDIA_TYPE_IMAGE) // Determine image info. imageInfo, err := GetImageInfo(filePath) if imageInfo != nil { file.SetWidth(int(imageInfo.Width)) file.SetHeight(int(imageInfo.Height)) } else { h.Registry().Logger().Warningf("Could not determine image info: %v", err) } } if strings.HasPrefix(mimeType, "video") { file.SetMediaType(MEDIA_TYPE_VIdEO) } // Store the file in the backend. backendId, writer, err2 := file.Writer(true) if err2 != nil { return apperror.Wrap(err2, "file_backend_error") } defer writer.Close() // Open file for reading. f, err := os.Open(filePath) if err != nil { return apperror.Wrap(err, "read_error", fmt.Sprintf("Could not read file at %v", filePath)) } _, err = io.Copy(writer, f) if err != nil { f.Close() return apperror.Wrap(err, "copy_to_backend_failed") } f.Close() // File is stored in backend now! file.SetBackendId(backendId) // Persist file to db. file.SetTmpPath("") if file.GetStrId() != "" { err2 = h.resource.Update(file, user) } else { err2 = h.resource.Create(file, user) } if err2 != nil { // Delete file from backend again. backend.DeleteFile(file) return apperror.Wrap(err2, "db_error", "Could not save file to database") } // Delete tmp file. if deleteFile { os.Remove(filePath) } if deleteDir { dir := strings.Join(pathParts[:len(pathParts)-1], string(os.PathSeparator)) os.RemoveAll(dir) } return nil }