问题所在

FastDFS采用的是tracker和storage的模式。

tracker负责负载均衡

storage负责文件存储、文件同步、提供文件访问接口(也就是说用户要从storage直接获取文件)

其实,storage的http端口是开着的,外界只要知道端口和文件路径信息就可以直接访问到文件。这种操作,对于文件而言是不安全的。

解决方式

在storage上配置访问规则,打开防盗链功能,首先要确保不能直接访问到文件。其次,限制访问文件时必须带有一个本地应用服务器生成的token,这个token需要有文件id、自己设置的密钥、时间戳三个部分组成。但是只有本地应用服务器保存密钥是不可行的,需要把密钥同时保存到storage上,用于token的解密验证工作。

配置

storage

进入etc/fdfs,编辑http.conf文件

编辑后的内容

Java

resources下新建fdfs_cilent.conf文件

#默认值为30s
connect_timeout = 10
#默认值为30s
network_timeout = 30
charset = UTF-8
#tracker的http端口
http.tracker_http_port = 8888
#服务器
tracker_server = 192.168.1.106:22122
#开启防盗链,防盗链密钥
http.anti_steal_token = true
http.secret_key= 密钥

主要的工具类文件

/**
 * 上传下载等文件操作工具类
 */
public class FastDFSClient {

    private static org.slf4j.Logger logger = LoggerFactory.getLogger(FastDFSClient.class);

    /***
     * 初始化加载FastDFS的TrackerServer配置
     */
    static {
        try {
            String filePath = new ClassPathResource("fdfs_client.conf").getFile().getAbsolutePath();
            ClientGlobal.init(filePath);
        } catch (Exception e) {
            logger.error("FastDFS Client Init Fail!",e);
        }
    }

    public static void main(String[] args) throws UnsupportedEncodingException, NoSuchAlgorithmException, MyException {
        String fileName = "M00/00/00/wKgBa2U4dPSAO7fXAAC5PC_5iKw01.docx";
        String groupName = "group1";
        String sourceUrl = getSourceUrl(groupName,fileName);
        System.out.println(sourceUrl);
    }

    public static FastDFSFile getFastDFSFile(MultipartFile file,String author) {
        InputStream inputStream = null;
        //获取文件名称(带有后缀)
        String allFileName = file.getOriginalFilename();
        //获取文件扩展名
        String ext = "";
        byte[] content = null;
        if (allFileName != null && !"".equals(allFileName)) {
            ext = (allFileName.substring(allFileName.lastIndexOf("."))).replaceAll("\\.","");
        }
        //文件名称(不带后缀)
        String fileName = allFileName.replaceAll("." + ext, "");
        try {
            inputStream = file.getInputStream();
            //获取文件所有的字节内容
            content = inputStream.readAllBytes();
            //一定要关闭输入流,不然会报错
            inputStream.close();
        } catch (IOException e) {
            throw new CustomException("文件上传错误", ResultCode.UPLOAD_ERROR);
        }
        //创建文件实体类对象
        FastDFSFile fastDFSFile = new FastDFSFile();
        fastDFSFile.setContent(content);
        fastDFSFile.setSize(content.length);
        fastDFSFile.setName(fileName);
        fastDFSFile.setExt(ext);
        fastDFSFile.setAuthor(author);
        return fastDFSFile;
    }

    /***
     * 文件上传
     * @param file
     * @return
     */
    public static String[] upload(FastDFSFile file) {
        //获取文件基础信息,有多少长度就要是多少,不然会报空指针
        NameValuePair[] meta_list = new NameValuePair[4];
        meta_list[0] = new NameValuePair("author", file.getAuthor());
        meta_list[1] = new NameValuePair("name",file.getName());
        meta_list[2] = new NameValuePair("ext",file.getExt());
        meta_list[3] = new NameValuePair("size", file.getSize()+"");
        //接收返回数据
        String[] uploadResults = null;
        StorageClient storageClient=null;
        try {
            //创建StorageClient客户端对象
            storageClient = getTrackerClient();
            /***
             * 文件上传
             * 1)文件字节数组
             * 2)文件扩展名
             * 3)文件作者
             */
            uploadResults = storageClient.upload_file(file.getContent(), file.getExt(), meta_list);
        } catch (Exception e) {
            logger.error("Exception when uploadind the file:" + file.getName(), e);
        }
        if (uploadResults == null && storageClient!=null) {
            logger.error("upload file fail, error code:" + storageClient.getErrorCode());
        }
        //获取组名
        String groupName = uploadResults[0];
        //获取文件存储路径
        String remoteFileName = uploadResults[1];
        return uploadResults;
    }

    /***
     * 获取文件信息
     * @param groupName:组名
     * @param remoteFileName:文件存储完整名
     * @return
     */
    public static FileInfo getFile(String groupName, String remoteFileName) {
        try {
            StorageClient storageClient = getTrackerClient();
            return storageClient.get_file_info(groupName, remoteFileName);
        } catch (Exception e) {
            logger.error("Exception: Get File from Fast DFS failed", e);
        }
        return null;
    }

    /***
     * 文件下载
     * @param groupName
     * @param remoteFileName
     * @return
     */
    public static InputStream downFile(String groupName, String remoteFileName) {
        try {
            //创建StorageClient
            StorageClient storageClient = getTrackerClient();
            //下载文件
            byte[] fileByte = storageClient.download_file(groupName, remoteFileName);
            InputStream ins = new ByteArrayInputStream(fileByte);
            return ins;
        } catch (Exception e) {
            logger.error("Exception: Get File from Fast DFS failed", e);
        }
        return null;
    }

    /***
     * 文件删除
     * @param groupName
     * @param remoteFileName
     * @throws Exception
     */
    public static void deleteFile(String groupName, String remoteFileName)
            throws Exception {
        //创建StorageClient
        StorageClient storageClient = getTrackerClient();
        //删除文件
        int i = storageClient.delete_file(groupName, remoteFileName);
    }

    /***
     * 获取Storage客户端
     * @return
     * @throws IOException
     */
    private static StorageClient getTrackerClient() throws IOException {
        TrackerServer trackerServer = getTrackerServer();
        StorageClient storageClient = new StorageClient(trackerServer, null);
        return  storageClient;
    }

    /***
     * 获取Tracker
     * @return
     * @throws IOException
     */
    private static TrackerServer getTrackerServer() throws IOException {
        TrackerClient trackerClient = new TrackerClient();
        TrackerServer trackerServer = trackerClient.getConnection();
        return  trackerServer;
    }

    /**
     * 生成防盗链token
     * @param groupName 文件组名称,group1
     * @param fileName 文件路径,不带group:M00/00/00/wKg4C1tFmTWAFPKBAADdeFFxlXA240.png
     * @return
     * @throws UnsupportedEncodingException
     * @throws NoSuchAlgorithmException
     * @throws MyException
     */
    public static String getSourceUrl(String groupName,String fileName) throws UnsupportedEncodingException, NoSuchAlgorithmException, MyException {
        //时间戳
        int lts = (int)(System.currentTimeMillis() / 1000);
        System.out.println(ClientGlobal.getG_secret_key());
        System.out.println("ip:"+FastDFSClient.getFile(groupName, fileName).getSourceIpAddr());
        //ip自动获取
        String ip = FastDFSClient.getFile(groupName, fileName).getSourceIpAddr();
        String token = ProtoCommon.getToken(fileName, lts, ClientGlobal.getG_secret_key()); //初始化secret_key
        //生成防盗链的文件访问url
        return "http://" +ip+ ":8888/" +groupName+"/" + fileName + "?token=" + token + "&ts=" + lts;
    }

    /***
     * 获取Storage组
     * @param groupName
     * @return
     * @throws IOException
     */
    public static StorageServer[] getStoreStorages(String groupName)
            throws IOException {
        //创建TrackerClient
        TrackerClient trackerClient = new TrackerClient();
        //获取TrackerServer
        TrackerServer trackerServer = trackerClient.getConnection();
        //获取Storage组
        return trackerClient.getStoreStorages(trackerServer, groupName);
    }

    /***
     * 获取Storage信息,IP和端口
     * @param groupName
     * @param remoteFileName
     * @return
     * @throws IOException
     */
    public static ServerInfo[] getFetchStorages(String groupName,String remoteFileName) throws IOException {
        TrackerClient trackerClient = new TrackerClient();
        TrackerServer trackerServer = trackerClient.getConnection();
        return trackerClient.getFetchStorages(trackerServer, groupName, remoteFileName);
    }

    /***
     * 获取Tracker服务地址
     * @return
     * @throws IOException
     */
    public static String getTrackerUrl() throws IOException {
        return "http://"+getTrackerServer().getInetSocketAddress().getHostString()+":"+ ClientGlobal.getG_tracker_http_port()+"/";
    }
}

附属的文件类

/**
 * 文件实体类
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class FastDFSFile {
    //文件名字
    private String name;

    //文件内容
    private byte[] content;

    //文件扩展名
    private String ext;

    //文件MD5摘要值
    private String md5;

    //文件创建作者
    private String author;

    //文件大小
    private Integer size;

    public FastDFSFile(String name, byte[] content, String ext) {
        super();
        this.name = name;
        this.content = content;
        this.ext = ext;
    }
}

参考资料

https://www.jianshu.com/p/bd077cdc2c82

https://www.cnblogs.com/fwfjava/p/17674975.html

Last modification:October 26th, 2023 at 03:45 pm