1 /*
2 * Copyright 2012-2025 CodeLibs Project and the Others.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
13 * either express or implied. See the License for the specific language
14 * governing permissions and limitations under the License.
15 */
16 package org.codelibs.fess.app.web.thumbnail;
17
18 import java.io.BufferedInputStream;
19 import java.io.File;
20 import java.io.FileInputStream;
21 import java.util.Map;
22
23 import org.codelibs.core.lang.StringUtil;
24 import org.codelibs.fess.app.web.base.FessSearchAction;
25 import org.codelibs.fess.util.DocumentUtil;
26 import org.lastaflute.web.Execute;
27 import org.lastaflute.web.response.ActionResponse;
28
29 import jakarta.annotation.Resource;
30 import jakarta.servlet.http.HttpServletResponse;
31
32 /**
33 * Action class for handling thumbnail image requests.
34 * Serves thumbnail images for documents in the search results.
35 */
36 public class ThumbnailAction extends FessSearchAction {
37
38 /**
39 * Default constructor for ThumbnailAction.
40 */
41 public ThumbnailAction() {
42 super();
43 }
44
45 // ===================================================================================
46 // Attribute
47 //
48 /**
49 * HTTP servlet response for streaming thumbnail images.
50 */
51 @Resource
52 protected HttpServletResponse response;
53
54 // ===================================================================================
55 // Hook
56 // ======
57
58 // ===================================================================================
59 // Search Execute
60 // ==============
61 /**
62 * Serves a thumbnail image for the specified document.
63 *
64 * @param form the thumbnail request form containing document ID and query parameters
65 * @return ActionResponse containing the thumbnail image or 404 if not found
66 */
67 @Execute
68 public ActionResponse index(final ThumbnailForm form) {
69 validate(form, messages -> {}, () -> asHtml(virtualHost(path_Error_ErrorJsp)));
70 if (isLoginRequired()) {
71 return redirectToLogin();
72 }
73
74 final Map<String, Object> doc =
75 searchHelper.getDocumentByDocId(form.docId, queryFieldConfig.getResponseFields(), getUserBean()).orElse(null);
76 final String url = DocumentUtil.getValue(doc, fessConfig.getIndexFieldThumbnail(), String.class);
77 if (StringUtil.isBlank(form.queryId) || StringUtil.isBlank(url) || !thumbnailSupport) {
78 // 404
79 throw responseManager.new404("Thumbnail for " + form.docId + " is not found.");
80 }
81
82 final File thumbnailFile = thumbnailManager.getThumbnailFile(doc);
83 if (thumbnailFile == null) {
84 if (fessConfig.isThumbnailEnabled()) {
85 thumbnailManager.offer(doc);
86 }
87 // 404
88 throw responseManager.new404("Thumbnail for " + form.docId + " is under generating.");
89 }
90
91 return asStream(form.docId).contentType(getImageMimeType(thumbnailFile)).stream(out -> {
92 try (BufferedInputStream in = new BufferedInputStream(new FileInputStream(thumbnailFile))) {
93 out.write(in);
94 }
95 });
96 }
97
98 /**
99 * Determines the MIME type of an image file based on its file extension.
100 *
101 * @param imageFile the image file
102 * @return the MIME type string
103 */
104 protected String getImageMimeType(final File imageFile) {
105 final String path = imageFile.getAbsolutePath();
106 if (path.endsWith(".png")) {
107 return "image/png";
108 }
109 if (path.endsWith(".gif")) {
110 return "image/gif";
111 }
112 if (path.endsWith(".jpg") || path.endsWith(".jpeg")) {
113 return "image/jpeg";
114 }
115 return "application/octet-stream";
116 }
117 }