需求:下载导入模板返回流的形式给前端
文件位置:项目resources文件下(这里没说写死文件路径的,个人想法不提倡)
坑:
- 使用TestFileUtil.class.getClassLoader().getResourceAsStream,inputStream.available()在windows下 获取流长度正常,但是在linux下获取长度为0,导致new byte[]长度为0,读不进去
- 在1的基础上in.available()方法修改为while循环读流的方式,但是这种方式读出来的流与正确的有差异
- 在2的基础上while循环读流的方式,用两个for循环解决,能解决,但是有点复杂
解决方案:使用getResourceAsStream,使用IoUtil.readBytes(输入流,是否关闭流)完美解决
接下来进行代码解说,先上解决方案,后面一步一步解说坑
前置准备
package com.***.vo.rsp;
import lombok.Data;
import java.io.Serializable;
/**
* @author longwei
* @Description 文件转base64DTO
* @date 2020/7/7 17:33
*/
@Data
public class FileBase64DTO implements Serializable {
private static final Long serialVersionUID = 1L;
// 文件转base64字符串
private String fileBaseStr;
// 文件名
private String fileName;
public FileBase64DTO(String fileBaseStr, String fileName) {
this.fileBaseStr = fileBaseStr;
this.fileName = fileName;
}
public static FileBase64DTO getFileBase64DTO(String fileBaseStr, String fileName) {
return new FileBase64DTO(fileBaseStr,fileName);
}
}
请求:
解决方案:使用getResourceAsStream,使用IoUtil.readBytes(输入流,是否关闭流)完美解决
package com.***.utils;
import cn.hutool.core.io.IoUtil;
import com.***.vo.rsp.FileBase64DTO;
import org.apache.commons.codec.binary.Base64;
import java.io.IOException;
import java.io.InputStream;
/**
* 文件上传工具类
*/
public class TestFileUtil {
/**
* 文件转为base64字符串
* 解决方案:使用ResourceAsStream,使用IoUtil.readBytes(输入流,是否关闭流)完美解决
* @param filePath 文件路径
* @param fileName 文件名
* @author longwei
*/
public static FileBase64DTO fileToBase64Str(String filePath, String fileName) {
InputStream in = null;
String base64;
try {
in = TestFileUtil.class.getClassLoader().getResourceAsStream(filePath);
byte[] bytes = IoUtil.readBytes(in, true);
base64 = new String(Base64.encodeBase64(bytes), "UTF-8");
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("下载文件失败,请相关人员检查");
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return FileBase64DTO.getFileBase64DTO(base64, fileName);
}
}
本地windows输出:
linux服务器输出:
以上为解决方案,接下来一步一步解说坑
1、使用TestFileUtil.class.getClassLoader().getResourceAsStream,inputStream.available()在windows下 获取流长度正常,但是在linux下获取长度为0,导致new byte[]长度为0,读不进去
/**
* 文件转为base64字符串
* 坑:在windows下 inputStream.available()获取流长度正常,但是在linux下获取长度为0,导致new byte[]长度为0,读不进去
* @param filePath 文件路径
* @param fileName 文件名
* @author longwei
*/
public static FileBase64DTO fileToBase64Str(String filePath, String fileName) {
InputStream in = null;
String base64;
try {
System.out.println("filePath:"+filePath);
System.out.println("==================");
in = TestFileUtil.class.getClassLoader().getResourceAsStream(filePath);
System.out.println("available:"+in.available());
System.out.println("==================");
byte[] bytes = new byte[in.available()];
in.read(bytes);
base64 = new String(Base64.encodeBase64(bytes), "UTF-8");
System.out.println("base64:"+base64);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("下载文件失败,请相关人员检查");
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return FileBase64DTO.getFileBase64DTO(base64, fileName);
}
本地windows输出:
linux服务器输出:in.available() 为0
2、在1的基础上in.available()方法修改为while循环读流的方式,但是这种方式读出来的流与正确的有差异
/**
* 文件转为base64字符串
* 在1的基础上in.available()方法修改为while循环读流的方式,但是这种方式读出来的流与正确的有差异
* @param filePath 文件路径
* @param fileName 文件名
* @author longwei
*/
public static FileBase64DTO fileToBase64Str2(String filePath, String fileName) {
StringBuilder stringBuilder=new StringBuilder();
InputStream in = null;
try {
ClassPathResource resource=new ClassPathResource(filePath);
in = resource.getInputStream();
byte[] bytes = new byte[1024];
int len = -1;
while ((len = in.read(bytes)) != -1) {
stringBuilder.append(new String(Base64.encodeBase64(bytes), "UTF-8"));
}
System.out.println(stringBuilder.toString());
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("下载文件失败,请相关人员检查");
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return FileBase64DTO.getFileBase64DTO(stringBuilder.toString(), fileName);
}
本地windows控制台输出:流差异
3、在2的基础上while循环读流的方式,用两个for循环解决,能解决,但是有点复杂
/**
* 文件转为base64字符串
* 在2的基础上while循环读流的方式,用两个for循环解决,能解决,但是有点复杂
* @param filePath 文件路径
* @param fileName 文件名
* @author longwei
*/
public static FileBase64DTO fileToBase64Str2(String filePath, String fileName) {
InputStream in = null;
String base64;
List<Byte> list=new LinkedList<>();
try {
in = TestFileUtil.class.getClassLoader().getResourceAsStream(filePath);
byte[] bys = new byte[1024];
int len; //len代表读取到的数据的长度
while ((len = in.read(bys)) != -1) {
for (int i = 0; i <len ; i++) {
list.add(bys[i]);
}
}
byte[] bytes=new byte[list.size()];
for (int i = 0; i <list.size() ; i++) {
bytes[i]=list.get(i);
}
base64 = new String(Base64.encodeBase64(bytes), "UTF-8");
System.out.println(base64);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("下载文件失败,请相关人员检查");
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return FileBase64DTO.getFileBase64DTO(base64, fileName);
}
本地windows控制台输出:正确
结论:使用getResourceAsStream,使用IoUtil.readBytes(输入流,是否关闭流)完美解决读文件流的问题