class FileAction extends Action {
    private static final org.slf4j.Logger Logger = LoggerFactory.getLogger(FileAction.class);

    @Override
    public boolean action(Request request, Response response) {<FILL_FUNCTION_BODY>}

    private static final Pattern INSECURE_URI = Pattern.compile(".*[<>&\"].*");
	private static final SimpleDateFormat HTTP_DATE_FORMATER = new SimpleDateFormat(HTTP_DATETIME_PATTERN, Locale.US);

	
	/**
	 * 通过URL中的path获得文件的绝对路径
	 * 
	 * @param httpPath Http请求的Path
	 * @return 文件绝对路径
	 */
	public static File getFileByPath(String httpPath) {
		// Decode the path.
		try {
			httpPath = URLDecoder.decode(httpPath, "UTF-8");
		} catch (UnsupportedEncodingException e) {
			throw new Error(e);
		}

		if (httpPath.isEmpty() || httpPath.charAt(0) != '/') {
			return null;
		}

		// 路径安全检查
        String path = httpPath.substring(0, httpPath.lastIndexOf("/"));
		if (path.contains("/.") || path.contains("./") || ReUtil.isMatch(INSECURE_URI, path)) {
			return null;
		}

		// 转换为绝对路径
		return FileUtil.file(ServerSetting.getRoot(), httpPath);
	}
}

        response.setHeader("Access-Control-Allow-Origin", "*");
        if (false == Request.METHOD_GET.equalsIgnoreCase(request.getMethod())) {
            response.sendError(HttpResponseStatus.METHOD_NOT_ALLOWED, "Please use GET method to request file!");
            return true;
        }

        if(ServerSetting.isRootAvailable() == false){
            response.sendError(HttpResponseStatus.NOT_FOUND, "404 Root dir not avaliable!");
            return true;
        }

        File file = null;
        try {
            file = getFileByPath(request.getPath());
        } catch (Exception e) {
            response.sendError(HttpResponseStatus.NOT_FOUND, "404 File not found!");
            return true;
        }

        // 隐藏文件或不存在，跳过
        if (file == null || file.isHidden() || !file.exists()) {
            response.sendError(HttpResponseStatus.NOT_FOUND, "404 File not found!");
            return true;
        }

        // 非文件，跳过
        if (!file.isFile()) {
            response.sendError(HttpResponseStatus.FORBIDDEN, "403 Forbidden!");
            return true;
        }

        Logger.debug("Client [{}] get file [{}]", request.getIp(), file.getPath());
        
        // Cache Validation
        String ifModifiedSince = request.getHeader(HttpHeaderNames.IF_MODIFIED_SINCE.toString());
        if (StrUtil.isNotBlank(ifModifiedSince)) {
            Date ifModifiedSinceDate = null;
            try {
                ifModifiedSinceDate = DateUtil.parse(ifModifiedSince, HTTP_DATE_FORMATER);
            } catch (Exception e) {
                Logger.warn("If-Modified-Since header parse error: {}", e.getMessage());
            }
            if(ifModifiedSinceDate != null) {
                // 只对比到秒一级别
                long ifModifiedSinceDateSeconds = ifModifiedSinceDate.getTime() / 1000;
                long fileLastModifiedSeconds = file.lastModified() / 1000;
                if (ifModifiedSinceDateSeconds == fileLastModifiedSeconds) {
                    Logger.debug("File {} not modified.", file.getPath());
                    response.sendNotModified();
                    return true;
                }
            }
        }

        response.setContent(file);
        response.setHeader("Content-Disposition", "attachment; filename=\"" + file.getName() + "\"");

        return true;
    
<methods>public non-sealed void <init>() ,public abstract boolean action(com.xiaoleilu.loServer.handler.Request, com.xiaoleilu.loServer.handler.Response) ,public boolean doAction(com.xiaoleilu.loServer.handler.Request, com.xiaoleilu.loServer.handler.Response) ,public static void init(io.moquette.server.config.IConfig) ,public boolean isTransactionAction() ,public cn.wildfirechat.common.ErrorCode preAction(com.xiaoleilu.loServer.handler.Request, com.xiaoleilu.loServer.handler.Response) <variables>protected static win.liyufan.im.RateLimiter adminLimiter,protected static win.liyufan.im.RateLimiter channelLimiter,protected static boolean closeApiVersion,public ChannelHandlerContext ctx,protected static final com.google.gson.Gson gson,public static io.moquette.spi.IMessagesStore messagesStore,protected static win.liyufan.im.RateLimiter robotLimiter,public static io.moquette.spi.ISessionsStore sessionsStore