001/**
002 * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com).
003 * <p>
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 * <p>
008 * http://www.apache.org/licenses/LICENSE-2.0
009 * <p>
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package io.jboot.web.attachment;
018
019import com.jfinal.log.Log;
020import com.jfinal.render.IRenderFactory;
021import com.jfinal.render.Render;
022import com.jfinal.render.RenderManager;
023
024import javax.servlet.http.HttpServletRequest;
025import javax.servlet.http.HttpServletResponse;
026import java.io.File;
027import java.io.InputStream;
028import java.util.HashMap;
029import java.util.List;
030import java.util.Map;
031import java.util.concurrent.CopyOnWriteArrayList;
032
033/**
034 * @author michael yang (fuhai999@gmail.com)
035 */
036public class AttachmentManager {
037
038    private static final Log LOG = Log.getLog(AttachmentManager.class);
039
040    private static Map<String, AttachmentManager> managers = new HashMap<>();
041
042    public static AttachmentManager me() {
043        return use("default");
044    }
045
046    public static AttachmentManager use(String name) {
047        AttachmentManager manager = managers.get(name);
048        if (manager == null) {
049            synchronized (AttachmentManager.class) {
050                manager = managers.get(name);
051                if (manager == null) {
052                    manager = new AttachmentManager(name);
053                    managers.put(name, manager);
054                }
055            }
056        }
057        return manager;
058    }
059
060    /**
061     * 通过这个方式可以来更改 manager 包括默认的 manager
062     *
063     * @param manager
064     */
065    public static void setManager(AttachmentManager manager) {
066        managers.put(manager.name, manager);
067    }
068
069
070    public AttachmentManager(String name) {
071        this.name = name;
072    }
073
074    /**
075     * 默认的 附件容器
076     */
077    protected LocalAttachmentContainer defaultContainer = new LocalAttachmentContainer();
078
079    /**
080     * 其他附件容器
081     */
082    protected List<AttachmentContainer> containers = new CopyOnWriteArrayList<>();
083
084    /**
085     * 自定义文件渲染器
086     */
087    protected IRenderFactory renderFactory = RenderManager.me().getRenderFactory();
088
089    /**
090     * manager  的名称
091     */
092    protected final String name;
093
094
095    public String getName() {
096        return name;
097    }
098
099    public IRenderFactory getRenderFactory() {
100        return renderFactory;
101    }
102
103    public void setRenderFactory(IRenderFactory renderFactory) {
104        this.renderFactory = renderFactory;
105    }
106
107
108    public LocalAttachmentContainer getDefaultContainer() {
109        return defaultContainer;
110    }
111
112    public void setDefaultContainer(LocalAttachmentContainer defaultContainer) {
113        this.defaultContainer = defaultContainer;
114    }
115
116    public void addContainer(AttachmentContainer container) {
117        containers.add(container);
118    }
119
120    public void setContainers(List<AttachmentContainer> containers) {
121        this.containers = containers;
122    }
123
124
125    public List<AttachmentContainer> getContainers() {
126        return containers;
127    }
128
129
130    /**
131     * 保存文件
132     *
133     * @param file
134     * @return 返回文件的相对路径
135     */
136    public String saveFile(File file) {
137        //优先从 默认的 container 去保存文件
138        String relativePath = defaultContainer.saveFile(file);
139        File defaultContainerFile = defaultContainer.getFile(relativePath);
140
141        for (AttachmentContainer container : containers) {
142            try {
143                if (container != defaultContainer) {
144                    container.saveFile(defaultContainerFile);
145                }
146            } catch (Exception ex) {
147                LOG.error("Save file error in container: " + container, ex);
148            }
149        }
150        return relativePath.replace("\\", "/");
151    }
152
153
154    /**
155     * 保存文件
156     *
157     * @param file
158     * @return 返回文件的相对路径
159     */
160    public String saveFile(File file, String toRelativePath) {
161        //优先从 默认的 container 去保存文件
162        String relativePath = defaultContainer.saveFile(file, toRelativePath);
163        File defaultContainerFile = defaultContainer.getFile(relativePath);
164
165        for (AttachmentContainer container : containers) {
166            try {
167                if (container != defaultContainer) {
168                    container.saveFile(defaultContainerFile, toRelativePath);
169                }
170            } catch (Exception ex) {
171                LOG.error("Save file error in container: " + container, ex);
172            }
173        }
174        return relativePath.replace("\\", "/");
175    }
176
177    /**
178     * 保存文件
179     *
180     * @param inputStream
181     * @return
182     */
183    public String saveFile(InputStream inputStream, String toRelativePath) {
184        //优先从 默认的 container 去保存文件
185        String relativePath = defaultContainer.saveFile(inputStream, toRelativePath);
186        File defaultContainerFile = defaultContainer.getFile(relativePath);
187
188        for (AttachmentContainer container : containers) {
189            try {
190                if (container != defaultContainer) {
191                    container.saveFile(defaultContainerFile, toRelativePath);
192                }
193            } catch (Exception ex) {
194                LOG.error("Save file error in container: " + container, ex);
195            }
196        }
197        return relativePath.replace("\\", "/");
198    }
199
200
201    /**
202     * 删除文件
203     *
204     * @param relativePath
205     * @return
206     */
207    public boolean deleteFile(String relativePath) {
208        for (AttachmentContainer container : containers) {
209            try {
210                container.deleteFile(relativePath);
211            } catch (Exception ex) {
212                LOG.error("Delete file error in container: " + container, ex);
213            }
214        }
215        return defaultContainer.deleteFile(relativePath);
216    }
217
218    /**
219     * 通过相对路径获取文件
220     *
221     * @param relativePath
222     * @return
223     */
224    public File getFile(String relativePath) {
225        //优先从 默认的 container 去获取
226        return getFile(relativePath, true);
227    }
228
229
230    /**
231     * 通过相对路径获取文件
232     *
233     * @param relativePath
234     * @param localFirst
235     * @return
236     */
237    public File getFile(String relativePath, boolean localFirst) {
238        //优先从 默认的 container 去获取
239        if (localFirst) {
240            File localFile = defaultContainer.getFile(relativePath);
241            if (localFile.exists()) {
242                return localFile;
243            }
244        }
245
246        for (AttachmentContainer container : containers) {
247            if (container != defaultContainer) {
248                try {
249                    File file = container.getFile(relativePath);
250                    if (file != null && file.exists()) {
251                        return file;
252                    }
253                } catch (Exception ex) {
254                    LOG.error("Get file error in container: " + container, ex);
255                }
256            }
257        }
258
259        //文件不存在,也返回该文件
260        return defaultContainer.getFile(relativePath);
261    }
262
263    /**
264     * 通过一个文件,获取其相对路径
265     *
266     * @param file
267     * @return
268     */
269    public String getRelativePath(File file) {
270        String relativePath = defaultContainer.getRelativePath(file);
271        return relativePath != null ? relativePath.replace("\\", "/") : null;
272    }
273
274
275    /**
276     * 创建一个新的文件
277     * 使用创建一般是创建一个空的文件,然后由外部逻辑进行写入
278     *
279     * @param suffix
280     * @return
281     */
282    public File createNewFile(String suffix) {
283        return getDefaultContainer().creatNewFile(suffix);
284    }
285
286
287    /**
288     * 渲染文件到浏览器
289     *
290     * @param target
291     * @param request
292     * @param response
293     * @return true 渲染成功,false 不进行渲染
294     */
295    public boolean renderFile(String target, HttpServletRequest request, HttpServletResponse response) {
296        if (target.startsWith(defaultContainer.getTargetPrefix())
297                && target.lastIndexOf('.') != -1) {
298            Render render;
299            if (target.contains("..")) {
300                render = renderFactory.getErrorRender(404);
301            } else {
302                File file = getFile(target);
303                render = getFileRender(file);
304            }
305            render.setContext(request, response).render();
306            return true;
307        } else {
308            return false;
309        }
310    }
311
312
313    protected Render getFileRender(File file) {
314        return file == null || !file.isFile()
315                ? renderFactory.getErrorRender(404)
316                : renderFactory.getFileRender(file);
317    }
318
319
320}