# 各种类型互转及在线文件图片处理

返回:专题

🐉 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) //文件流
);
1
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);
1
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/") // 文件存储位置
);
1
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" // 文件名
);
1
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);
1
2
3
4
5

# MultipartFile类型转File类型

File destFile = new File("tmp/source/destFile");
if (!fileSourcePath.exists()) {
fileSourcePath.mkdirs();
}
// 将MultipartFile存到临时文件中
mulFileSource.transferTo(destFile);
1
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);
1
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);
1
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());
    }

1
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())
1
2
3
4

# BufferedImage 与 byte[] 互转

ImageIO.write(BufferedImage image,String format,OutputStream out);
1
  • 参数image表示获得的BufferedImage;
  • 参数format表示图片的格式,比如“gif”等;
  • 参数out表示输出流,如果要转成Byte数组,则输出流为ByteArrayOutputStream即可;
//将b作为输入流;
ByteArrayInputStream in = new ByteArrayInputStream(byte[]b);
//将in作为输入流,读取图片存入image中,而这里in可以为ByteArrayInputStream();
BufferedImage image = ImageIO.read(InputStream in);
1
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;
}
1
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;
}
1
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;
}
1
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();
}
1
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();
1
2
3
4
5

inputStream.available())此方法对本地文件不会存在流不完整的问题,不能用在接收url文件,因为是分片传输

# 数字类型之间相互转换

# long(Long)与int(Integer)相互转换

一.将long型转化为int型,这里的long型是基础类型:

long   a = 10;     int b = (int)a;
1

二.将Long型转换为int 型的,这里的Long型是包装类型:

Long a = 10; int b=a.intValue();
1

三.将int型转化为long型,这里的int型是基础类型:

int a = 10;long b = (long)a;
1

四.将Integer型转化为long型,这里的Integer型是包装类型:

int a = 10;Long b = a.longValue();
1

# 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);
1
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
1
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);
1
2
3
4
11:37:16.838 [main] INFO com.me.JustTest - 先转为更高精度f3:127.1,d3:127.1
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();
}
1
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();
}
1
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()));
  }
1
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));
  }
1
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));
    }
1
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);
1
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]));
1
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();
1
2
3
4
5
6
7
8
9
10
11

包装类型一般都有parseLong的parseXXX的静态方法,不过一般只能传String类型。

# JSONObject相关转换

# java对象与json对象

JSONObject configObject = (JSONObject) JSON.toJSON(deviceVo);
1

# 日期时间相关的转换

关联阅读java时间相关转换

# 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());
1
2
3
4
5
6
7
8
9
10
11

# LocalDate与时间戳

// 时间戳转LocalDate同LocalDateTime

// 转时间戳
System.out.println(LocalDate.now().atStartOfDay().toInstant(ZoneOffset.ofHours(8)).toEpochMilli());
1
2
3
4

# LocalDateTime与Date

LocalDateTime dateLocal = date.toInstant().atZone(ZoneOffset.ofHours(8)).toLocalDateTime();

Date dateDate = Date.from(now.atZone(ZoneOffset.ofHours(8)).toInstant());
1
2
3

# LocalDate与Date

Date dateDate = Date.from(now..atStartOfDay((ZoneOffset.ofHours(8)).toInstant());
1

# 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
1
2
3
4
5
6
7
8