美文网首页
解决FastDFS 不同局域网上传下载文件无法正常访问/获取 S

解决FastDFS 不同局域网上传下载文件无法正常访问/获取 S

作者: 清晨先生2 | 来源:发表于2019-03-08 20:28 被阅读0次

    前言

    前几天自己搭建了一个小的练手项目,其中文件存储使用了 FastDFS 文件服务器 ,但是在实际部署时出现了问题,由于项目是部署在阿里云服务器上,FastDFS服务器部署在本地局域网通过端口路由的方式进行文件操作 ,两台服务器不在同一个局域网,导致项目连接Fdfs服务器时获取到的Storage存储节点为该局域网ip 上传下载文件都timeout。

    后面查了下fastdfs-client的 源码 发现 里面获取Storage存储节点时是从 tracker里获取的 代码如下:

        /**
         * 获取存储Group
         *
         * @param groupName
         * @return
         */
        private StorageNode getStorageNode(String groupName) {
            if (null == groupName) {
                return trackerClient.getStoreStorage();
            } else {
                return trackerClient.getStoreStorage(groupName);
            }
        }
    

    既然找到了问题的所在,那就好解决了,话不多说 上代码

    添加配置:

    fdfs.storage.node.flag=true
    fdfs.storage.node.server.ip=192.168.8.18
    fdfs.storage.node.server.port=23000
    

    重写 Fdfs获取StorageClient节点的实现

    package com.hm.www.admin.config;
    
    import com.github.tobato.fastdfs.FdfsClientConstants;
    import com.github.tobato.fastdfs.domain.fdfs.MetaData;
    import com.github.tobato.fastdfs.domain.fdfs.StorageNode;
    import com.github.tobato.fastdfs.domain.fdfs.StorePath;
    import com.github.tobato.fastdfs.domain.fdfs.ThumbImageConfig;
    import com.github.tobato.fastdfs.domain.proto.storage.StorageSetMetadataCommand;
    import com.github.tobato.fastdfs.domain.proto.storage.StorageUploadFileCommand;
    import com.github.tobato.fastdfs.domain.proto.storage.StorageUploadSlaveFileCommand;
    import com.github.tobato.fastdfs.domain.proto.storage.enums.StorageMetadataSetType;
    import com.github.tobato.fastdfs.domain.upload.FastFile;
    import com.github.tobato.fastdfs.domain.upload.FastImageFile;
    import com.github.tobato.fastdfs.domain.upload.ThumbImage;
    import com.github.tobato.fastdfs.exception.FdfsUnsupportImageTypeException;
    import com.github.tobato.fastdfs.exception.FdfsUploadImageException;
    import com.github.tobato.fastdfs.service.DefaultGenerateStorageClient;
    import com.github.tobato.fastdfs.service.FastFileStorageClient;
    import net.coobird.thumbnailator.Thumbnails;
    import org.apache.commons.io.IOUtils;
    import org.apache.commons.lang3.StringUtils;
    import org.apache.commons.lang3.Validate;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.context.annotation.Primary;
    import org.springframework.stereotype.Component;
    
    import java.io.ByteArrayInputStream;
    import java.io.ByteArrayOutputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.util.Arrays;
    import java.util.List;
    import java.util.Set;
    
    /**
     * @author yangxiaohui
     * @Date: Create by 2019-03-08 13:26
     * @Description: FastDFS获取的节点数据配置为正常的
     */
    @Primary
    @Component("myFastFileStorageClient")
    public class MyFastFileStorageClient extends DefaultGenerateStorageClient implements FastFileStorageClient {
        /**
         * 支持的图片类型
         */
        private static final List<String> SUPPORT_IMAGE_LIST = Arrays.asList(FdfsClientConstants.SUPPORT_IMAGE_TYPE);
        /**
         * 缩略图生成配置
         */
        @Autowired
        private ThumbImageConfig thumbImageConfig;
        @Value("${fdfs.storage.node.flag}")
        private boolean defaultStorageFlag;
        @Value("${fdfs.storage.node.server.ip}")
        private String defaultStorageIp;
        @Value("${fdfs.storage.node.server.port}")
        private int defaultStoragePort;
        /**
         * 上传文件
         */
        @Override
        public StorePath uploadFile(InputStream inputStream, long fileSize,
                                    String fileExtName, Set<MetaData> metaDataSet) {
            FastFile fastFile;
            if (null == metaDataSet) {
                fastFile = new FastFile.Builder()
                        .withFile(inputStream, fileSize, fileExtName)
                        .build();
            } else {
                fastFile = new FastFile.Builder()
                        .withFile(inputStream, fileSize, fileExtName)
                        .withMetaData(metaDataSet)
                        .build();
            }
            return uploadFile(fastFile);
        }
    
        /**
         * 上传图片并且生成缩略图
         */
        @Override
        public StorePath uploadImageAndCrtThumbImage(InputStream inputStream,
                                                     long fileSize,
                                                     String fileExtName,
                                                     Set<MetaData> metaDataSet) {
            FastImageFile fastImageFile;
            if (null == metaDataSet) {
                fastImageFile = new FastImageFile.Builder()
                        .withFile(inputStream, fileSize, fileExtName)
                        .withThumbImage()
                        .build();
            } else {
                fastImageFile = new FastImageFile.Builder()
                        .withFile(inputStream, fileSize, fileExtName)
                        .withMetaData(metaDataSet)
                        .withThumbImage()
                        .build();
            }
            return uploadImage(fastImageFile);
        }
    
        /**
         * 上传文件
         * <pre>
         * 可通过fastFile对象配置
         * 1. 上传图像分组
         * 2. 上传元数据metaDataSet
         * <pre/>
         * @param fastFile
         * @return
         */
        @Override
        public StorePath uploadFile(FastFile fastFile) {
            Validate.notNull(fastFile.getInputStream(), "上传文件流不能为空");
            Validate.notBlank(fastFile.getFileExtName(), "文件扩展名不能为空");
            // 获取存储节点
            StorageNode client = getStorageNode(fastFile.getGroupName());
            // 上传文件
            return uploadFileAndMetaData(client, fastFile.getInputStream(),
                    fastFile.getFileSize(), fastFile.getFileExtName(),
                    fastFile.getMetaDataSet());
        }
    
        /**
         * 上传图片
         * <pre>
         * 可通过fastImageFile对象配置
         * 1. 上传图像分组
         * 2. 上传元数据metaDataSet
         * 3. 是否生成缩略图
         *   3.1 根据默认配置生成缩略图
         *   3.2 根据指定尺寸生成缩略图
         *   3.3 根据指定比例生成缩略图
         * <pre/>
         * @param fastImageFile
         * @return
         */
        @Override
        public StorePath uploadImage(FastImageFile fastImageFile) {
            String fileExtName = fastImageFile.getFileExtName();
            Validate.notNull(fastImageFile.getInputStream(), "上传文件流不能为空");
            Validate.notBlank(fileExtName, "文件扩展名不能为空");
            // 检查是否能处理此类图片
            if (!isSupportImage(fileExtName)) {
                throw new FdfsUnsupportImageTypeException("不支持的图片格式" + fileExtName);
            }
            // 获取存储节点
            StorageNode client = getStorageNode(fastImageFile.getGroupName());
            byte[] bytes = inputStreamToByte(fastImageFile.getInputStream());
    
            // 上传文件和metaDataSet
            StorePath path = uploadFileAndMetaData(client, new ByteArrayInputStream(bytes),
                    fastImageFile.getFileSize(), fileExtName,
                    fastImageFile.getMetaDataSet());
    
            //如果设置了需要上传缩略图
            if (null != fastImageFile.getThumbImage()) {
                // 上传缩略图
                uploadThumbImage(client, new ByteArrayInputStream(bytes), path.getPath(), fastImageFile);
            }
            bytes = null;
            return path;
        }
    
    
        /**
         * 获取存储Group
         *
         * @param groupName
         * @return
         */
        private StorageNode getStorageNode(String groupName) {
            StorageNode storageNode = null;
            if (null == groupName) {
                storageNode = trackerClient.getStoreStorage();
            } else {
                storageNode = trackerClient.getStoreStorage(groupName);
            }
            if (defaultStorageFlag) {
                if (!StringUtils.isEmpty(defaultStorageIp)) {
                    storageNode.setIp(defaultStorageIp);
                }
                if (defaultStoragePort > 0) {
                    storageNode.setPort(defaultStoragePort);
                }
            }
            return storageNode;
        }
    
        /**
         * 获取byte流
         *
         * @param inputStream
         * @return
         */
        private byte[] inputStreamToByte(InputStream inputStream) {
            try {
                return IOUtils.toByteArray(inputStream);
            } catch (IOException e) {
                LOGGER.error("image inputStream to byte error", e);
                throw new FdfsUploadImageException("upload ThumbImage error", e.getCause());
            }
        }
    
        /**
         * 检查是否有MetaData
         *
         * @param metaDataSet
         * @return
         */
        private boolean hasMetaData(Set<MetaData> metaDataSet) {
            return null != metaDataSet && !metaDataSet.isEmpty();
        }
    
        /**
         * 是否是支持的图片文件
         *
         * @param fileExtName
         * @return
         */
        private boolean isSupportImage(String fileExtName) {
            return SUPPORT_IMAGE_LIST.contains(fileExtName.toUpperCase());
        }
    
        /**
         * 上传文件和元数据
         *
         * @param client
         * @param inputStream
         * @param fileSize
         * @param fileExtName
         * @param metaDataSet
         * @return
         */
        private StorePath uploadFileAndMetaData(StorageNode client, InputStream inputStream, long fileSize,
                                                String fileExtName, Set<MetaData> metaDataSet) {
            // 上传文件
            StorageUploadFileCommand command = new StorageUploadFileCommand(client.getStoreIndex(), inputStream,
                    fileExtName, fileSize, false);
            StorePath path = connectionManager.executeFdfsCmd(client.getInetSocketAddress(), command);
            // 上传metadata
            if (hasMetaData(metaDataSet)) {
                StorageSetMetadataCommand setMDCommand = new StorageSetMetadataCommand(path.getGroup(), path.getPath(),
                        metaDataSet, StorageMetadataSetType.STORAGE_SET_METADATA_FLAG_OVERWRITE);
                connectionManager.executeFdfsCmd(client.getInetSocketAddress(), setMDCommand);
            }
            return path;
        }
    
        /**
         * 上传缩略图
         *
         * @param client
         * @param inputStream
         * @param masterFilename
         * @param fastImageFile
         */
        private void uploadThumbImage(StorageNode client, InputStream inputStream,
                                      String masterFilename, FastImageFile fastImageFile) {
            ByteArrayInputStream thumbImageStream = null;
            ThumbImage thumbImage = fastImageFile.getThumbImage();
            try {
                //生成缩略图片
                thumbImageStream = generateThumbImageStream(inputStream, thumbImage);
                // 获取文件大小
                long fileSize = thumbImageStream.available();
                // 获取配置缩略图前缀
                String prefixName = thumbImage.getPrefixName();
                LOGGER.error("获取到缩略图前缀{}", prefixName);
                StorageUploadSlaveFileCommand command = new StorageUploadSlaveFileCommand(thumbImageStream, fileSize,
                        masterFilename, prefixName, fastImageFile.getFileExtName());
                connectionManager.executeFdfsCmd(client.getInetSocketAddress(), command);
    
            } catch (IOException e) {
                LOGGER.error("upload ThumbImage error", e);
                throw new FdfsUploadImageException("upload ThumbImage error", e.getCause());
            } finally {
                IOUtils.closeQuietly(thumbImageStream);
            }
        }
    
        /**
         * 上传缩略图
         *
         * @param client
         * @param inputStream
         * @param masterFilename
         * @param fileExtName
         */
        private void uploadThumbImage(StorageNode client, InputStream inputStream, String masterFilename,
                                      String fileExtName) {
            ByteArrayInputStream thumbImageStream = null;
            try {
                thumbImageStream = generateThumbImageByDefault(inputStream);// getFileInputStream
                // 获取文件大小
                long fileSize = thumbImageStream.available();
                // 获取缩略图前缀
                String prefixName = thumbImageConfig.getPrefixName();
                LOGGER.debug("上传缩略图主文件={},前缀={}", masterFilename, prefixName);
                StorageUploadSlaveFileCommand command = new StorageUploadSlaveFileCommand(thumbImageStream, fileSize,
                        masterFilename, prefixName, fileExtName);
                connectionManager.executeFdfsCmd(client.getInetSocketAddress(), command);
    
            } catch (IOException e) {
                LOGGER.error("upload ThumbImage error", e);
                throw new FdfsUploadImageException("upload ThumbImage error", e.getCause());
            } finally {
                IOUtils.closeQuietly(thumbImageStream);
            }
        }
    
    
        /**
         * 生成缩略图
         *
         * @param inputStream
         * @param thumbImage
         * @return
         * @throws IOException
         */
        private ByteArrayInputStream generateThumbImageStream(InputStream inputStream,
                                                              ThumbImage thumbImage) throws IOException {
            //根据传入配置生成缩略图
            if (thumbImage.isDefaultConfig()) {
                //在中间修改配置,这里不是一个很好的实践,如果有时间再进行优化
                thumbImage.setDefaultSize(thumbImageConfig.getWidth(), thumbImageConfig.getHeight());
                return generateThumbImageByDefault(inputStream);
            } else if (thumbImage.getPercent() != 0) {
                return generateThumbImageByPercent(inputStream, thumbImage);
            } else {
                return generateThumbImageBySize(inputStream, thumbImage);
            }
    
        }
    
        /**
         * 根据传入比例生成缩略图
         *
         * @param inputStream
         * @param thumbImage
         * @return
         * @throws IOException
         */
        private ByteArrayInputStream generateThumbImageByPercent(InputStream inputStream,
                                                                 ThumbImage thumbImage) throws IOException {
            LOGGER.debug("根据传入比例生成缩略图");
            // 在内存当中生成缩略图
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            //@formatter:off
            Thumbnails
                    .of(inputStream)
                    .scale(thumbImage.getPercent())
                    .toOutputStream(out);
            //@formatter:on
            return new ByteArrayInputStream(out.toByteArray());
        }
    
        /**
         * 根据传入尺寸生成缩略图
         *
         * @param inputStream
         * @param thumbImage
         * @return
         * @throws IOException
         */
        private ByteArrayInputStream generateThumbImageBySize(InputStream inputStream,
                                                              ThumbImage thumbImage) throws IOException {
            LOGGER.debug("根据传入尺寸生成缩略图");
            // 在内存当中生成缩略图
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            //@formatter:off
            Thumbnails
                    .of(inputStream)
                    .size(thumbImage.getWidth(), thumbImage.getHeight())
                    .toOutputStream(out);
            //@formatter:on
            return new ByteArrayInputStream(out.toByteArray());
        }
    
        /**
         * 获取缩略图
         *
         * @param inputStream
         * @return
         * @throws IOException
         */
        private ByteArrayInputStream generateThumbImageByDefault(InputStream inputStream) throws IOException {
            LOGGER.debug("根据默认配置生成缩略图");
            // 在内存当中生成缩略图
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            //@formatter:off
            Thumbnails
                    .of(inputStream)
                    .size(thumbImageConfig.getWidth(), thumbImageConfig.getHeight())
                    .toOutputStream(out);
            //@formatter:on
            return new ByteArrayInputStream(out.toByteArray());
        }
    
        /**
         * 删除文件
         */
        @Override
        public void deleteFile(String filePath) {
            StorePath storePath = StorePath.parseFromUrl(filePath);
            super.deleteFile(storePath.getGroup(), storePath.getPath());
        }
    
        public String getDefaultStorageIp() {
            return defaultStorageIp;
        }
    
        public void setDefaultStorageIp(String defaultStorageIp) {
            this.defaultStorageIp = defaultStorageIp;
        }
    
        public int getDefaultStoragePort() {
            return defaultStoragePort;
        }
    
        public void setDefaultStoragePort(int defaultStoragePort) {
            this.defaultStoragePort = defaultStoragePort;
        }
    }
    
    

    搞定!重启项目 上传,下载为所欲为。

    注:

    1. 因为Fdfs本身是没有密码的,所以大多都是局域网使用,还请谨慎暴露外网。
    2. 本项目为练手项目,Storage节点也就一个,所以没有考虑多个存储节点分配的问题。在此也就抛砖引玉了。写的不好,大佬们勿喷。

    相关文章

      网友评论

          本文标题:解决FastDFS 不同局域网上传下载文件无法正常访问/获取 S

          本文链接:https://www.haomeiwen.com/subject/zhudpqtx.html