# 各种类型互转及在线文件图片处理
| 🐉 java8时间中的各种转换 | 🐉 工具类实现大转换 |
|---|
# 转换成MultiPartFile
# File类型转Multipartfile
MultipartFile接口有两个常用实现类,
MockMultipartFile和CommonsMultipartFile
- 使用MockMultipartFile
看名字就知道MockMultipartFile主要用于测试用途(项目发布后发现项目启动不了一直报错找不到MockMultipartFile,原因是发布正式版时、根本不会打测试包。所以找不到。),但是相对CommonsMultipartFile来说,创建相当简便。
File file = new File("/Users/coderec/Desktop/haha.jpg");
MultipartFile mulFile = new MockMultipartFile(
"haha.jpg", //文件名
"haha.jpg", //originalName 相当于上传文件在客户机上的文件名
ContentType.APPLICATION_OCTET_STREAM.toString(), //文件类型
new FileInputStream(file) //文件流
);
2
3
4
5
6
7
- 使用CommonsMultipartFile
与MockMultipartFile相比,CommonsMultipartFile仅仅有一个构造方法:CommonsMultipartFile(FileItem fileItem)
因此,若要使用CommonsMultipartFile来创建MultipartFile就一定要使用FileItem。通过FileItem将File转化为MultipartFile的过程如下伪代码所示:
File file = new File("temp/fileItem/haha.jpg");
if (!file.exists()) {
file.mkdirs();
}
// 创建fileItem,具体方法参考下面
FileItem fileItem = new ...;
// 将File内容写入fileItem,使用org.apache.commons.io.IOUtils
IOUtils.copy(new FileInputStream(file), fileItem.getOutputStream());
// 创建multipartfile
MultipartFile multipartFile = new CommonsMultipartFile(fileItem);
2
3
4
5
6
7
8
9
10
# 如何创建FileItem
FileItem接口只有一个实现类:
DiskFileItem
- 直接使用DiskFileItem创建
// 创建fileItem
FileItem fileItem = new DiskFileItem(
"file", // 表单参数名
ContentType.APPLICATION_OCTET_STREAM.toString(), // 文件类型
false, // 是否为表单格式
RandomKeyUtils.genRandomKey() + ".jpg", // 文件名
10240, // 超过多少byte存在磁盘上
new file("tmp/fileItem/") // 文件存储位置
);
2
3
4
5
6
7
8
9
- 使用DiskFileItemFactory创建
// 小于5M文件都在内存中,否则存入硬盘
final int tmpFileSize = 5242880;
// 设置临时文件大小以及临时文件存储路径
DiskFileItemFactory fileItemFactory = new DiskFileItemFactory(tmpFileSize, new file("tmp/fileItem/"));
// 创建fileItem
FileItem fileItem = fileItemFactory.createItem(
"file", // 表单参数名
ContentType.APPLICATION_OCTET_STREAM.toString(), // 文件类型
false, // 是否为表单格式
RandomKeyUtils.genRandomKey() + ".jpg" // 文件名
);
2
3
4
5
6
7
8
9
10
11
# DiskFileItem产生的临时文件处理
- 使用内存存储,不写入硬盘
- 在创建FileItem时,可以设定一个较大的文件大小,使文件不被写入硬盘,就不会产生临时文件的问题。不过这不是一个正确的解决问题的方式。而且,也会受到内存大小限制。
- 使用FileCleaningTracker
- 在使用DiskFileItemFactory时,应该会发现其有一个void setFileCleaningTracker(FileCleaningTracker pTracker)方法,此方法就是为临时文件设置监听线程,一旦发现临时文件被垃圾回收,就会清除临时文件。
我们可以通过FileCleanerCleanup监听器以及ServletContext获得监听线程FileCleaningTracker。
为FileItem设置监听的过程如下:
FileCleaningTracker fileCleaningTracker
= FileCleanerCleanup.getFileCleaningTracker(servletContext);
DiskFileItemFactory factory
= new DiskFileItemFactory(10240, File("tmp/fileItem/");
factory.setFileCleaningTracker(fileCleaningTracker);
2
3
4
5
# MultipartFile类型转File类型
File destFile = new File("tmp/source/destFile");
if (!fileSourcePath.exists()) {
fileSourcePath.mkdirs();
}
// 将MultipartFile存到临时文件中
mulFileSource.transferTo(destFile);
2
3
4
5
6
# 流与图片、文件等互转
# InputStream 与 base64 互转
ByteArrayInputStream bis = null;
byte[] bytes = Base64.getDecoder().decode("");
bis = new ByteArrayInputStream(bytes);
InputStream is = new FileInputStream("");
byte[] bytes = null;
ByteArrayOutputStream bos = new ByteArrayOutputStream();
byte[] buf = new byte[128];
int rc = 0;
while ((rc = is.read(buf, 0, 128)) > 0) {
bos.write(buf, 0, rc);
}
bytes = bos.toByteArray();
String s = Base64.getEncoder().encodeToString(bytes);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# InputStream 与 byte[] 互转
IOUtils.toByteArray(cpr.getInputStream());
byte[] byt = new byte[1024];
InputStream ins = new ByteArrayInputStream(byt);
2
3
4
5
# InputStream 与 ByteArrayOutputStream 互转
public Reader(InputStream input) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len;
try {
while ((len = input.read(buffer)) > -1) {
baos.write(buffer, 0, len);
}
baos.flush();
} catch (IOException e) {
throw new Exception("Illegal flow.");
} finally {
try {
input.close();
} catch (IOException e) {
logger.error("file stream shutdown failed.");
}
}
this.baos = baos;
}
private InputStream streamTran(ByteArrayOutputStream in) {
return new ByteArrayInputStream(in.toByteArray());
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# BufferedImage 转换成 InputStream
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ImageOutputStream ios = ImageIO.createImageOutputStream(bos);
ImageIO.write(bufImg,"jpg",ios);
InputStream bis = new ByteArrayInputStream(bos.toByteArray())
2
3
4
# BufferedImage 与 byte[] 互转
ImageIO.write(BufferedImage image,String format,OutputStream out);
- 参数image表示获得的BufferedImage;
- 参数format表示图片的格式,比如“gif”等;
- 参数out表示输出流,如果要转成Byte数组,则输出流为ByteArrayOutputStream即可;
//将b作为输入流;
ByteArrayInputStream in = new ByteArrayInputStream(byte[]b);
//将in作为输入流,读取图片存入image中,而这里in可以为ByteArrayInputStream();
BufferedImage image = ImageIO.read(InputStream in);
2
3
4
# BufferedImage 与 Image 互转
public class BufferedImage extends java.awt.Image implements WritableRenderedImage, Transparency
/**
* 将image对象 转成 BufferedImage
*
* @param image
* @return
*/
private BufferedImage toBufferedImage(Image image) {
if (image instanceof BufferedImage) {
return (BufferedImage) image;
}
image = new ImageIcon(image).getImage();
BufferedImage bimage = null;
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
try {
int transparency = Transparency.OPAQUE;
GraphicsDevice gs = ge.getDefaultScreenDevice();
GraphicsConfiguration gc = gs.getDefaultConfiguration();
bimage = gc.createCompatibleImage(
image.getWidth(null), image.getHeight(null), transparency);
} catch (HeadlessException e) {
//........
}
if (bimage == null) {
int type = BufferedImage.TYPE_INT_RGB;
bimage = new BufferedImage(image.getWidth(null), image.getHeight(null), type);
}
Graphics g = bimage.createGraphics();
g.drawImage(image, 0, 0, null);
g.dispose();
return bimage;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# 获取URL图片
图片较小的话,可以直接一次性拿到图片流,但是
图片大了以后,服务器是分片传输,如果只拿一次流,则会导致拿到的图片不完整
# DataInputStream
private static byte[] getBase64ByDataInput(String urlPath) throws IOException {
URL url = new URL(urlPath);
DataInputStream dataInputStream = new DataInputStream(url.openStream());
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
byte[] bytes = new byte[2048];
int len = 0;
while ((len=dataInputStream.read(bytes)) > 0){
outputStream.write(bytes,0,len);
}
dataInputStream.close();
byte[] results = outputStream.toByteArray();
outputStream.close();
return results;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
WARNING
获取到的图片流会偏大,信息取多。。
# BufferedImage InputStream
private static byte[] getBase64ByUrl(String urlPath) throws IOException {
if (StringUtils.isBlank(urlPath)){
return null;
}
final String defaultSuffix = "jpg";
List<String> suffixes = Arrays.asList("png","jpg","gif","jpeg","bmp");
String suffix = Optional.of(urlPath).filter(StringUtils::isNotBlank)
.map(str -> StringUtils.substring(str,str.lastIndexOf(".")+1))
.filter(suffixes::contains)
.orElse(defaultSuffix);
URL url = new URL(urlPath);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod(RequestMethod.GET.name());
conn.setConnectTimeout(5000);
conn.setRequestProperty("Accept-Encoding", "identity");
BufferedImage image=ImageIO.read(conn.getInputStream());
ByteArrayOutputStream baso = new ByteArrayOutputStream();
ImageIO.write(image,StringUtils.defaultIfBlank(suffix,"jpg"),baso);
conn.disconnect();
byte[] results = baso.toByteArray();
baso.close();
return results;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
TIP
此方法拿到的流不会出任何问题
# 获取本地资源文件
# 本地图片资源
private static byte[] getBase64ByLocal(String imgPath) throws IOException {
String suffix = Optional.ofNullable(imgPath).filter(StringUtils::isNotBlank)
.map(str -> StringUtils.substring(str,str.lastIndexOf(".")+1))
.orElse(StringUtils.EMPTY);
File file = new File(imgPath.trim());
BufferedImage bi;
bi = ImageIO.read(file);
ByteArrayOutputStream baso = new ByteArrayOutputStream();
ImageIO.write(bi,suffix,baso);
return baso.toByteArray();
}
2
3
4
5
6
7
8
9
10
11
File file = new File(imgPath);
InputStream inputStream = new FileInputStream(file);
byte[] imgByte = new byte[inputStream.available()];
inputStream.read(imgByte);
inputStream.close();
2
3
4
5
(
inputStream.available())此方法对本地文件不会存在流不完整的问题,不能用在接收url文件,因为是分片传输
# 数字类型之间相互转换
# long(Long)与int(Integer)相互转换
一.将long型转化为int型,这里的long型是基础类型:
long a = 10; int b = (int)a;
二.将Long型转换为int 型的,这里的Long型是包装类型:
Long a = 10; int b=a.intValue();
三.将int型转化为long型,这里的int型是基础类型:
int a = 10;long b = (long)a;
四.将Integer型转化为long型,这里的Integer型是包装类型:
int a = 10;Long b = a.longValue();
# double与float互相转换
为什么double转float不会出现数据误差,而float转double却误差如此之大?
double d1 = 3.14;
float f1 = (float) d1;
log.info("double:{},float:{}",d1,f1);
float f2 = 127.1f;
double d2 = f2;
log.info("float:{},double:{}",f2,d2);
2
3
4
5
6
7
11:34:36.296 [main] INFO com.me.JustTest - double:3.14,float:3.14
11:34:36.321 [main] INFO com.me.JustTest - float:127.1,double:127.0999984741211
2
如何避免这样的问题发生,让float转double能得到实际的数据?
解决办法:现将float型转换为字符串型,再转换为精度更高的BigDecimal型,再将其转换为double型。
float f3 = 127.1f;
BigDecimal b = new BigDecimal(String.valueOf(f3));
double d3 = b.doubleValue();
log.info("先转为更高精度f3:{},d3:{}",f3,d3);
2
3
4
11:37:16.838 [main] INFO com.me.JustTest - 先转为更高精度f3:127.1,d3:127.1
/**
- 提供精確的加法運算
*/
public static double add(double v1, double v2) {
BigDecimal b1 = new BigDecimal(Double.toString(v1));
BigDecimal b2 = new BigDecimal(Double.toString(v2));
return b1.add(b2).doubleValue();
}
/**
- 提供了精確的減法運算
*
*/
public static double sub(double v1, double v2) {
BigDecimal b1 = new BigDecimal(Double.toString(v1));
BigDecimal b2 = new BigDecimal(Double.toString(v2));
return b1.subtract(b2).doubleValue();
}
/**
- 提供了精確的乘法運算
*/
public static double mul(double v1, double v2) {
BigDecimal b1 = new BigDecimal(Double.toString(v1));
BigDecimal b2 = new BigDecimal(Double.toString(v2));
return b1.multiply(b2).doubleValue();
}
/**
- 提供了(相對)精確的除法運算,當發生除不儘的情況時,精確到
- 小數點以後110位
*/
public static double div(double v1, double v2) {
return div(v1, v2, 10);
}
/**
- 提供了(相對)精確的除法運算,當發生除不儘的情況時,由scale參數指定
- 精度,以後的數字四捨五入
*/
public static double div(double v1, double v2, int scale) {
if (scale < 0) {
throw new IllegalArgumentException(
"The scale must be a positive integer or zero");
}
BigDecimal b1 = new BigDecimal(Double.toString(v1));
BigDecimal b2 = new BigDecimal(Double.toString(v2));
return b1.divide(b2, scale, BigDecimal.ROUND_HALF_UP).doubleValue();
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# String与byte[]相互转换
互转时,出现乱码问题,解决方案。
/**
- 获取byte[]实际长度
- @param bytes
- @return
*/
public static int getValidLength(byte[] bytes){
int i = 0;
if (null == bytes || 0 == bytes.length)
return i ;
for (; i < bytes.length; i++) {
if (bytes[i] == '\0')
break;
}
return i + 1;
}
/**
- 获取byte实际值
- @param bytes
- @return
*/
public static String getCopyByte(byte[] bytes){
if (null == bytes || 0 == bytes.length) {
return "";
}
int length = getValidLength(bytes);
byte[] bb = new byte[length];
System.arraycopy(bytes, 0, bb, 0, length);
String result = new String(bb);
return result.trim();
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
以上两步,即可获取其中实际字符串内容。
# java8 Stream大转换
如何生成stream参照java8中的stream章节——stream的创建方式
# Stream转换为List
public static void main(String[] args) {
List<String> names = Arrays.asList("Chris", "HTML", "XML", "CSS");
Stream<String> s = names.stream().filter(name -> name.startsWith("C"));
System.out.println(s.collect(Collectors.toList()));
}
2
3
4
5
# Stream转换为Set
public static void main(String[] args) {
Stream<String> words = Stream.of("All", "men", "are", "created", "equal");
Set<String> wordsSet = words.collect(Collectors.toSet());
wordsSet.forEach(n -> System.out.println(n));
}
2
3
4
5
# Stream转换为类型数组
public static void main(String[] args) {
// collect as typed array
Stream<String> words = Stream.of("All", "men", "are", "created", "equal");
final String[] wordsArray = words.toArray(String[]::new);
Arrays.asList(wordsArray).forEach(n-> System.out.println(n));
}
2
3
4
5
6
# List大转换转换
# List与Set互相转换
通过构造方法
List<String> list = new ArrayList<>(Arrays.asList("12","12","31","13"));
String[] strArray = ArrayUtils.toArray("12","12","31","13");
Set<String> sets = new HashSet<>(list);
2
3
# List与Array
Criteria criteria = new Criteria();
List<Criteria> queryCriterias = new ArrayList<>();
queryCriterias.add(...);
queryCriterias.add(...);
queryCriterias.add(...);
criteria.andOperator(queryCriterias.toArray(new Criteria[0]));
2
3
4
5
6
# Java中long(Long)与int(Integer)之间的转换
long a = 10;
int b = (int)a;
Long a = 10;
int b=a.intValue();
int a = 10;
long b = (long)a;
int a = 10;
Long b = a.longValue();
2
3
4
5
6
7
8
9
10
11
包装类型一般都有parseLong的parseXXX的静态方法,不过一般只能传String类型。
# JSONObject相关转换
# java对象与json对象
JSONObject configObject = (JSONObject) JSON.toJSON(deviceVo);
# 日期时间相关的转换
# LocalDateTime与时间戳
LocalDateTime now = LocalDateTime.now();
System.out.println(now.toEpochSecond(ZoneOffset.ofHours(8)));
System.out.println(now.toInstant(ZoneOffset.UTC).getEpochSecond());
System.out.println(now.toInstant(ZoneOffset.UTC).toEpochMilli());
// 1615861725
// 1615890525
// 1615890525466
System.out.println(LocalDateTime.ofEpochSecond(timeStampSecond, 0, ZoneOffset.ofHours(8)));
System.out.println(Instant.ofEpochMilli(timeStampSecond).atZone(ZoneId.systemDefault()).toLocalDateTime());
2
3
4
5
6
7
8
9
10
11
# LocalDate与时间戳
// 时间戳转LocalDate同LocalDateTime
// 转时间戳
System.out.println(LocalDate.now().atStartOfDay().toInstant(ZoneOffset.ofHours(8)).toEpochMilli());
2
3
4
# LocalDateTime与Date
LocalDateTime dateLocal = date.toInstant().atZone(ZoneOffset.ofHours(8)).toLocalDateTime();
Date dateDate = Date.from(now.atZone(ZoneOffset.ofHours(8)).toInstant());
2
3
# LocalDate与Date
Date dateDate = Date.from(now..atStartOfDay((ZoneOffset.ofHours(8)).toInstant());
# LocalDateTime与String(OffsetDateTime)
LocalDateTime beginTime = LocalDateTime.parse(beginStr, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
beginTime.atOffset(ZoneOffset.ofHours(8)).format(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSXXX"));
// 2021-07-19T11:46:13.831+08:00
String time = "2021-07-15T23:59:58.000+08:00";
System.out.println(OffsetDateTime.parse(time).toLocalDateTime());
// 2021-07-15T23:59:58
2
3
4
5
6
7
8