| 
									
										
										
										
											2020-07-28 09:57:26 +00:00
										 |  |  | //go:generate go-enum --sql --marshal --names --file $GOFILE
 | 
					
						
							| 
									
										
										
										
											2020-06-25 07:37:13 +00:00
										 |  |  | package http | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							| 
									
										
										
										
											2020-07-27 17:01:02 +00:00
										 |  |  | 	"bytes" | 
					
						
							| 
									
										
										
										
											2020-07-23 00:41:19 +00:00
										 |  |  | 	"context" | 
					
						
							| 
									
										
										
										
											2020-06-25 07:37:13 +00:00
										 |  |  | 	"fmt" | 
					
						
							| 
									
										
										
										
											2020-07-23 00:41:19 +00:00
										 |  |  | 	"io" | 
					
						
							| 
									
										
										
										
											2020-06-25 07:37:13 +00:00
										 |  |  | 	"net/http" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	"github.com/gorilla/mux" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	"github.com/filebrowser/filebrowser/v2/files" | 
					
						
							| 
									
										
										
										
											2020-07-23 00:41:19 +00:00
										 |  |  | 	"github.com/filebrowser/filebrowser/v2/img" | 
					
						
							| 
									
										
										
										
											2020-06-25 07:37:13 +00:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-28 09:57:26 +00:00
										 |  |  | /* | 
					
						
							|  |  |  | ENUM( | 
					
						
							|  |  |  | thumb | 
					
						
							|  |  |  | big | 
					
						
							| 
									
										
										
										
											2020-06-25 07:37:13 +00:00
										 |  |  | ) | 
					
						
							| 
									
										
										
										
											2020-07-28 09:57:26 +00:00
										 |  |  | */ | 
					
						
							|  |  |  | type PreviewSize int | 
					
						
							| 
									
										
										
										
											2020-06-25 07:37:13 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-23 00:41:19 +00:00
										 |  |  | type ImgService interface { | 
					
						
							|  |  |  | 	FormatFromExtension(ext string) (img.Format, error) | 
					
						
							| 
									
										
										
										
											2020-07-24 18:08:26 +00:00
										 |  |  | 	Resize(ctx context.Context, in io.Reader, width, height int, out io.Writer, options ...img.Option) error | 
					
						
							| 
									
										
										
										
											2020-07-23 00:41:19 +00:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2020-06-25 07:37:13 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-27 17:01:02 +00:00
										 |  |  | type FileCache interface { | 
					
						
							|  |  |  | 	Store(ctx context.Context, key string, value []byte) error | 
					
						
							|  |  |  | 	Load(ctx context.Context, key string) ([]byte, bool, error) | 
					
						
							| 
									
										
										
										
											2020-07-28 09:57:26 +00:00
										 |  |  | 	Delete(ctx context.Context, key string) error | 
					
						
							| 
									
										
										
										
											2020-07-27 17:01:02 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func previewHandler(imgSvc ImgService, fileCache FileCache, enableThumbnails, resizePreview bool) handleFunc { | 
					
						
							| 
									
										
										
										
											2020-07-23 00:41:19 +00:00
										 |  |  | 	return withUser(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) { | 
					
						
							|  |  |  | 		if !d.user.Perm.Download { | 
					
						
							|  |  |  | 			return http.StatusAccepted, nil | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		vars := mux.Vars(r) | 
					
						
							| 
									
										
										
										
											2020-07-28 09:57:26 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		previewSize, err := ParsePreviewSize(vars["size"]) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return http.StatusBadRequest, err | 
					
						
							| 
									
										
										
										
											2020-07-23 00:41:19 +00:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-06-25 07:37:13 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-23 00:41:19 +00:00
										 |  |  | 		file, err := files.NewFileInfo(files.FileOptions{ | 
					
						
							| 
									
										
										
										
											2021-01-07 10:30:17 +00:00
										 |  |  | 			Fs:         d.user.Fs, | 
					
						
							|  |  |  | 			Path:       "/" + vars["path"], | 
					
						
							|  |  |  | 			Modify:     d.user.Perm.Modify, | 
					
						
							|  |  |  | 			Expand:     true, | 
					
						
							|  |  |  | 			ReadHeader: d.server.TypeDetectionByHeader, | 
					
						
							|  |  |  | 			Checker:    d, | 
					
						
							| 
									
										
										
										
											2020-07-23 00:41:19 +00:00
										 |  |  | 		}) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return errToStatus(err), err | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-06-25 07:37:13 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-23 00:41:19 +00:00
										 |  |  | 		setContentDisposition(w, r, file) | 
					
						
							| 
									
										
										
										
											2020-06-25 07:37:13 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-23 00:41:19 +00:00
										 |  |  | 		switch file.Type { | 
					
						
							|  |  |  | 		case "image": | 
					
						
							| 
									
										
										
										
											2020-07-28 09:57:26 +00:00
										 |  |  | 			return handleImagePreview(w, r, imgSvc, fileCache, file, previewSize, enableThumbnails, resizePreview) | 
					
						
							| 
									
										
										
										
											2020-07-23 00:41:19 +00:00
										 |  |  | 		default: | 
					
						
							|  |  |  | 			return http.StatusNotImplemented, fmt.Errorf("can't create preview for %s type", file.Type) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2020-06-25 07:37:13 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-27 17:01:02 +00:00
										 |  |  | func handleImagePreview(w http.ResponseWriter, r *http.Request, imgSvc ImgService, fileCache FileCache, | 
					
						
							| 
									
										
										
										
											2020-07-28 09:57:26 +00:00
										 |  |  | 	file *files.FileInfo, previewSize PreviewSize, enableThumbnails, resizePreview bool) (int, error) { | 
					
						
							| 
									
										
										
										
											2020-07-23 00:41:19 +00:00
										 |  |  | 	format, err := imgSvc.FormatFromExtension(file.Extension) | 
					
						
							| 
									
										
										
										
											2020-06-25 07:37:13 +00:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		// Unsupported extensions directly return the raw data
 | 
					
						
							| 
									
										
										
										
											2020-07-23 00:41:19 +00:00
										 |  |  | 		if err == img.ErrUnsupportedFormat { | 
					
						
							| 
									
										
										
										
											2020-06-25 07:37:13 +00:00
										 |  |  | 			return rawFileHandler(w, r, file) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return errToStatus(err), err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-28 09:57:26 +00:00
										 |  |  | 	cacheKey := previewCacheKey(file.Path, previewSize) | 
					
						
							| 
									
										
										
										
											2020-07-27 17:01:02 +00:00
										 |  |  | 	cachedFile, ok, err := fileCache.Load(r.Context(), cacheKey) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return errToStatus(err), err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if ok { | 
					
						
							|  |  |  | 		_, _ = w.Write(cachedFile) | 
					
						
							|  |  |  | 		return 0, nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-07 14:47:11 +00:00
										 |  |  | 	fd, err := file.Fs.Open(file.Path) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return errToStatus(err), err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	defer fd.Close() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-23 00:41:19 +00:00
										 |  |  | 	var ( | 
					
						
							|  |  |  | 		width   int | 
					
						
							|  |  |  | 		height  int | 
					
						
							|  |  |  | 		options []img.Option | 
					
						
							|  |  |  | 	) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-23 10:38:03 +00:00
										 |  |  | 	switch { | 
					
						
							| 
									
										
										
										
											2020-07-28 09:57:26 +00:00
										 |  |  | 	case previewSize == PreviewSizeBig && resizePreview && format != img.FormatGif: | 
					
						
							| 
									
										
										
										
											2020-07-23 00:41:19 +00:00
										 |  |  | 		width = 1080 | 
					
						
							|  |  |  | 		height = 1080 | 
					
						
							| 
									
										
										
										
											2020-07-23 10:38:03 +00:00
										 |  |  | 		options = append(options, img.WithMode(img.ResizeModeFit), img.WithQuality(img.QualityMedium)) | 
					
						
							| 
									
										
										
										
											2020-07-28 09:57:26 +00:00
										 |  |  | 	case previewSize == PreviewSizeThumb && enableThumbnails: | 
					
						
							| 
									
										
										
										
											2020-07-23 00:41:19 +00:00
										 |  |  | 		width = 128 | 
					
						
							|  |  |  | 		height = 128 | 
					
						
							| 
									
										
										
										
											2020-07-23 10:38:03 +00:00
										 |  |  | 		options = append(options, img.WithMode(img.ResizeModeFill), img.WithQuality(img.QualityLow), img.WithFormat(img.FormatJpeg)) | 
					
						
							| 
									
										
										
										
											2020-06-25 07:37:13 +00:00
										 |  |  | 	default: | 
					
						
							| 
									
										
										
										
											2020-07-23 10:38:03 +00:00
										 |  |  | 		if _, err := rawFileHandler(w, r, file); err != nil { | 
					
						
							|  |  |  | 			return errToStatus(err), err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return 0, nil | 
					
						
							| 
									
										
										
										
											2020-06-25 07:37:13 +00:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-27 17:01:02 +00:00
										 |  |  | 	buf := &bytes.Buffer{} | 
					
						
							|  |  |  | 	if err := imgSvc.Resize(context.Background(), fd, width, height, buf, options...); err != nil { | 
					
						
							|  |  |  | 		return 0, err | 
					
						
							| 
									
										
										
										
											2020-06-25 07:37:13 +00:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-07-27 17:01:02 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	go func() { | 
					
						
							|  |  |  | 		if err := fileCache.Store(context.Background(), cacheKey, buf.Bytes()); err != nil { | 
					
						
							|  |  |  | 			fmt.Printf("failed to cache resized image: %v", err) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	}() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	_, _ = w.Write(buf.Bytes()) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-25 07:37:13 +00:00
										 |  |  | 	return 0, nil | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2020-07-28 09:57:26 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | func previewCacheKey(fPath string, previewSize PreviewSize) string { | 
					
						
							|  |  |  | 	return fPath + previewSize.String() | 
					
						
							|  |  |  | } |