利用Java实现后端文件跨服务器传输(上传图片等文件)

背景

假设一台服务器部署了一个 Java 应用程序,需要将本机的数据文件(可能是文本也可能是图像)上传到远端的另外一台服务器,注意这个不是通过前端进行PUT请求来完成的,需要怎么做呢?

实现

需要上传的一方实现一个 FileIOService.java


    // 这里把图片的常用后缀采用列举的方式做了设置,可以换个其他更简单的图像判别方法
    private static final String[] IMAGE_VALUES = new String[]{"jpg", "BMP", "bmp", "JPG", "wbmp", "jpeg", "png", "PNG", "JPEG", "WBMP", "GIF", "gif"};
    private static final HashSet<String> IMAGE_TYPE = new HashSet<>(Arrays.asList(IMAGE_VALUES));

    /**
     * 将文件转换成 byte[]
     *
     * @param filePath 文件路径
     * @return buffer
     */
    private byte[] getBytes(String filePath) throws IOException {
        byte[] buffer = new byte[0];
        FileInputStream fileInputStream = null;
        ByteArrayOutputStream byteArrayOutputStream = null;
        try {
            File file = new File(filePath);
            fileInputStream = new FileInputStream(file);
            byteArrayOutputStream = new ByteArrayOutputStream(1024);
            byte[] b = new byte[1024];
            int n;
            while ((n = fileInputStream.read(b)) != -1) {
                byteArrayOutputStream.write(b, 0, n);
            }
            buffer = byteArrayOutputStream.toByteArray();
        } catch (Exception e) {
            log.error(e);
        } finally {
            if (fileInputStream != null) fileInputStream.close();
            if (byteArrayOutputStream != null) byteArrayOutputStream.close();
        }
        return buffer;
    }

    // 获取文件字节数组
    private byte[] getFileBytes(File file) throws IOException {
        String fileName = file.getName();
        String[] names = fileName.split("\\.");
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        if (names.length > 1) {
            String fileType = names[names.length - 1];
            if (IMAGE_TYPE.contains(fileType)) {
                log.info("The file {} is a picture.", fileName);
                BufferedImage bi;
                try {
                    bi = ImageIO.read(file);
                    ImageIO.write(bi, fileType, byteArrayOutputStream);
                    byte[] bytes = byteArrayOutputStream.toByteArray();
                    byteArrayOutputStream.close();
                    return bytes;
                } catch (IOException e) {
                    log.error(e);
                }
            }
        }
        return getBytes(file.getAbsolutePath());
    }

  /**
     * 将文件流发送至另外服务器的方法
     *
     * @param bytes    文件字节
     * @param fileName  文件路径
     * @return 从服务器端响应的流 可通过 new String(bytes); 转换
     */
    private void httpPost(byte[] bytes, String fileName) throws IOException {
        try {
            URL console = new URL(LOCALIZATION_UPLOAD_URL);
            HttpURLConnection conn = (HttpURLConnection) console.openConnection();
            conn.addRequestProperty("fileName", fileName);
            conn.setConnectTimeout(30000);
            conn.setReadTimeout(30000);
            conn.setUseCaches(false);
            conn.setDoOutput(true);
            conn.setDoInput(true);
            conn.setRequestMethod("POST");
            conn.connect();
            DataOutputStream out = new DataOutputStream(conn.getOutputStream());
            log.info(bytes.length);
            out.write(bytes);
            out.flush();
            out.close();
            InputStream is = conn.getInputStream();
            if (is != null) {
                ByteArrayOutputStream outStream = new ByteArrayOutputStream();
                byte[] buffer = new byte[1024];
                int len = 0;
                while ((len = is.read(buffer)) != -1) {
                    outStream.write(buffer, 0, len);
                }
                is.close();
                conn.disconnect();
                byte[] response = outStream.toByteArray();
                if (response.length > 0) {
                    log.info("Upload response: {}", new String(response));
                }
            }
            conn.disconnect();
        } catch (Exception e) {
            log.error("Upload failed: {}", e);
        }
    }

    // 支持多文件上传
    private void multipleFileUpload(File file) {
        if (file.isFile()) {
            log.info("upload file name {}", file.getName());
            uploadFile(file, file.getName());
            return;
        }
        File[] files = file.listFiles();
        if (files == null) return;
        for (File f : files) {
            multipleFileUpload(f);
        }
    }

    // 上传单文件
    private void uploadFile(File file) {
        try {
            byte[] bytes = getFileBytes(file);
            log.info("Upload file : {}", file.getName());
            httpPost(bytes, file.getName());
        } catch (IOException e) {
            log.error(e);
        }
    }
    

远端服务器服务接受数据流并持久化到磁盘

// FileIOUtils.java 下面会用到的获取服务器上根目录所在位置的工具类
public class FileIOUtils {
    private FileIOUtils() {
        throw new IllegalStateException("Utility class");
    }

    public static File getRootPath() throws FileNotFoundException {
        File path = new File(ResourceUtils.getURL("classpath:").getPath());
        if(!path.exists()) {
            path = new File("");
        }
        return path;
    }
}
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
    /**
     * 判断指定文件夹是否存在,不存在则创建
     * @param path 路径前缀
     */
    private void createDirIfNoExist(String path) {
        File file = new File(path, FilenameUtils.getName(path));
        if(!file.exists()) {
            boolean res = file.mkdirs();
            log.info("set writable {}", file.setWritable(true, false));
            log.info("create dir {} : {}", file.getAbsoluteFile(), res);
        }
    }
    
    @RequestMapping("upload")
    public void receiveFile(HttpServletRequest request) throws IOException {
        ServletInputStream in = request.getInputStream();
        try {
            String fileName = request.getHeader("fileName");
            String path = FileIOUtils.getRootPath().getAbsolutePath() + "/static/upload/";
            String name = FilenameUtils.getName(fileName);
            String filePath = path + fileName.replace(name, "");
            createDirIfNoExist(filePath);
            File file = new File(filePath , FilenameUtils.getName(fileName));
            if (!file.exists()) {
                log.info("create file {} : {}", fileName, file.createNewFile());
            }
            log.info("set file writable {}", file.setWritable(true, false));
            FileUtils.copyInputStreamToFile(in, file);
        } catch (Exception e) {
            log.error(e);
        }
    }

在 SpringBoot 框架下,可能会有异常: request.getInputStream() 获取不到数据(返回空值),这是因为在框架里,HttpServletRequest 已经被框架在接收 HTTP Request 时读取过一遍了,然后我们自己要读取的话,数据流已经被处理过(处理过一次,InputStream的 read index 已经为 -1,再直接读就为空了),这种情况下需要加一个Wrapper类,在第一次读取时对数据进行一次备份
InputStreamHttpServletRequestWrapper.java

public class InputStreamHttpServletRequestWrapper extends HttpServletRequestWrapper {
    private final byte[] body;
    private static final int BUFFER_SIZE = 4096;

    public InputStreamHttpServletRequestWrapper(HttpServletRequest request) throws IOException {
        super(request);
        body = inputStream2Byte(request.getInputStream());

    }

    private byte[] inputStream2Byte(InputStream inputStream) throws IOException {
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        byte[] bytes = new byte[BUFFER_SIZE];
        int length;
        while ((length = inputStream.read(bytes, 0, BUFFER_SIZE)) != -1) {
            outputStream.write(bytes, 0, length);
        }

        return outputStream.toByteArray();
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {
        ByteArrayInputStream inputStream = new ByteArrayInputStream(body);

        return new ServletInputStream() {
            @Override
            public boolean isFinished() {
                return false;
            }

            @Override
            public boolean isReady() {
                return false;
            }

            @Override
            public void setReadListener(ReadListener readListener) {
                log.info("set readListener");
            }

            @Override
            public int read() throws IOException {
                return inputStream.read();
            }
        };
    }

    @Override
    public BufferedReader getReader() throws IOException {
        return new BufferedReader(new InputStreamReader(getInputStream()));
    }
}
  • 9
    点赞
  • 60
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值