byte[]/InputStream/MultipartFile之间进行转换

embedded/2024/10/11 1:07:35/

前言

问题产生:

最近开发项目的时候,遇到了文件上传对象转换的问题 -> 我在对接抖音开放平台的时候,有一个图片上传的接口,需要将byte[]转为MultipartFile 对象,但是发现根本没有这样的工具类,后面翻阅了不少帖子得到了解决方案。


需求是这样的:

阿里云的OSS对象存储照片URL需要通过Java获取到图片流,不过我这里下载的是byte数组,byte数组与流对象之间的转换还是比较简单的,这里我就是希望 byte 数组转为MultipartFile对象:

获取图片的字节数据:

java">// 图片的链接
String url = "https://profile-avatar.csdnimg.cn/6337cc80d5124929b02f7a57c790083a_qq_31762741.jpg"; 
// 下载图片为字节数组
byte[] bytes = HttpUtil.downloadBytes(url);

然后再将图片流上传到抖音的服务器,但是抖音的上传图片接口长这样:

上传对象是 MultipartFile,而我的图片资源又是 byte 数组,这之间就需要做一个转换了。


说明

教程中大量使用到了Hutool工具:

<dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.8.11</version>
</dependency>

byte[]转MultipartFile

在将byte[]转换为MultipartFile时,可以通过以下步骤完成:

  1. 导入相关依赖:确保项目中引入了Spring框架的相关依赖,以及文件上传所需的其他依赖。
  2. 创建一个实现了MultipartFile接口的自定义类:由于MultipartFile是一个接口,无法直接实例化,因此需要创建一个自定义类来实现该接口。
  3. 实现自定义类的相关方法:自定义类需要实现MultipartFile接口中的方法,包括getName()getOriginalFilename()getContentType()getSize()getInputStream()等方法。其中,getInputStream()方法需要将byte[]转换为InputStream

以下是一个示例代码,展示了如何将byte[]转换为MultipartFile

java">package com.hsqyz.common.utils.file;import org.springframework.web.multipart.MultipartFile;import java.io.*;
import java.nio.file.Files;/*** 实现将字节数组转换为 MultipartFile 的工具类。** @author 花伤情犹在*/
public class ByteToMultipartFile implements MultipartFile, Serializable {private final byte[] content; // 存储字节数组的内容private final String name; // MultipartFile 的名称private final String originalFilename; // 原始文件名private final String contentType; // 文件的内容类型/*** 构造函数,初始化 MultipartFile 对象。** @param content          字节数组内容* @param name             MultipartFile 的名称* @param originalFilename 原始文件名* @param contentType      文件的内容类型*/public ByteToMultipartFile(byte[] content, String name, String originalFilename, String contentType) {this.content = content;this.name = name;this.originalFilename = originalFilename;this.contentType = contentType;}/*** 获取 MultipartFile 的名称。** @return MultipartFile 的名称*/@Overridepublic String getName() {return name;}/*** 获取原始文件名。** @return 原始文件名*/@Overridepublic String getOriginalFilename() {return originalFilename;}/*** 获取文件的内容类型。** @return 文件的内容类型*/@Overridepublic String getContentType() {return contentType;}/*** 判断 MultipartFile 是否为空。** @return 如果字节数组长度为 0,则返回 true;否则返回 false*/@Overridepublic boolean isEmpty() {return this.content.length == 0;}/*** 获取文件的大小(字节数)。** @return 文件的大小*/@Overridepublic long getSize() {return this.content.length;}/*** 获取字节数组内容。** @return 字节数组内容* @throws IOException 如果发生 I/O 错误*/@Overridepublic byte[] getBytes() throws IOException {return this.content;}/*** 获取输入流。** @return 输入流* @throws IOException 如果发生 I/O 错误*/@Overridepublic InputStream getInputStream() throws IOException {return new ByteArrayInputStream(this.content);}/*** 将文件内容保存到指定位置。** @param dest 目标文件路径* @throws IOException           如果发生 I/O 错误* @throws IllegalStateException 如果文件状态不正确*/@Overridepublic void transferTo(File dest) throws IOException, IllegalStateException {// 实现文件的保存操作,可根据具体需求自行实现// 例如:Files.write(dest.toPath(), content);Files.write(dest.toPath(), content);}}

使用该自定义类时,可以通过以下方式将byte[]转换为MultipartFile

java">byte[] fileBytes = // 从某处获取到的byte[]数据
String name = // 表单字段的名称
String fileName = // 文件名
String contentType = // 文件类型// 转换数据
MultipartFile multipartFile = new ByteToMultipartFile(fileBytes, "file", fileName, contentType);

可以看到这里仍然还需要一些数据,这里声明出来:

变量说明
fileBytes这个字节数据就是我们通过 Hutool 的下载工具已经拿到了【HttpUtil.downloadBytes(url)】
name这个字段名称对应接口的表单字段名
fileName文件名字可以选择截取 oss 图片 url 路径上的文件名
contentType响应内容类型也是可以通过响应头拿到的

fileBytes:

这里直接使用 Hutool 的请求下载工具来获取 urlbyte 字节数据 。

java">// 图片的链接
String url = "https://profile-avatar.csdnimg.cn/6337cc80d5124929b02f7a57c790083a_qq_31762741.jpg"; 
// 下载图片为字节数组
byte[] bytes = HttpUtil.downloadBytes(url);

name:

例如抖音上传图片接口的二进制图片字段名为 Image,那我们这里也写死为 Image 即可。


fileName:

我们的 oss 图片对象通常都是文件名跟随在 url 路径后面的,例如阿里云 oss:

我们可以通过截取 url 里面最后一个/反斜杠的方式获取文件名,可以看到我们下载下来的文件名字就是最后一个/反斜杠后面的内容:

这里封装了一个截取的 url 最后一个/反斜杠后面内容的工具类:

java">import cn.hutool.core.util.URLUtil;/*** 文件名工具类** @author 花伤情犹在*/
public class FileNameUtils {/*** 从 URL 中获取文件名。** @param url 资源的 URL* @return 文件名字符串,如果获取失败返回 null*/public static String getFileNameFromUrl(String url) {if (url == null || url.isEmpty()) {throw new IllegalArgumentException("URL 不能为空");}try {// 解析 URLjava.net.URL parsedUrl = URLUtil.url(url);// 获取路径部分String path = parsedUrl.getPath();// 提取文件名int lastSlashIndex = path.lastIndexOf('/');if (lastSlashIndex >= 0 && lastSlashIndex < path.length() - 1) {return path.substring(lastSlashIndex + 1);}} catch (Exception e) {e.printStackTrace();}return null;}public static void main(String[] args) {// 示例 URLString imageUrl = "https://example.com/path/to/image.jpg"; // 替换为你实际的图片 URL// 获取文件名String fileName = FileNameUtils.getFileNameFromUrl(imageUrl);if (fileName != null) {System.out.println("文件名: " + fileName);} else {System.out.println("无法获取文件名");}}}

contentType:

这里封转了一个工具类发起请求可以直接进行从响应头里面获取响应内容类型:

java">import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import cn.hutool.http.Method;/*** 获取 URL 资源的 ContentType。** @author 花伤情犹在*/
public class ContentTypeUtils {/*** 获取 URL 资源的 ContentType。** @param url 资源的 URL* @return ContentType 字符串,如果获取失败返回 null*/public static String getContentTypeFromUrl(String url) {if (url == null || url.isEmpty()) {throw new IllegalArgumentException("URL 不能为空");}try {// 发送 HEAD 请求以获取响应头信息HttpResponse response = HttpUtil.createRequest(Method.GET, url).execute();// 检查响应状态码是否为 200if (response.getStatus() == 200) {// 从响应头中获取 ContentTypereturn response.header("Content-Type");} else {System.err.println("无法获取资源,状态码: " + response.getStatus());}} catch (Exception e) {e.printStackTrace();}return null;}public static void main(String[] args) {// 示例 URLString imageUrl = "https://xx.com?test.jpg"; // 替换为你实际的图片 URL// 获取 ContentTypeString contentType = ContentTypeUtils.getContentTypeFromUrl(imageUrl);if (contentType != null) {System.out.println("Content-Type: " + contentType);} else {System.out.println("无法获取 Content-Type");}}}

当然这里还封装了一个MultipartFile 转换工具类,不需要手动实现 MultipartFile 接口来自己造实现类:

注意需要依赖:commons-fileupload

<!-- 文件上传 -->
<dependency><groupId>commons-fileupload</groupId><artifactId>commons-fileupload</artifactId><version>1.5</version><scope>compile</scope>
</dependency>

工具代码如下:

java">import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.commons.CommonsMultipartFile;import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;/*** MultipartFile 转换工具类** @author 花伤情犹在*/
public class MultipartFileConverter {/*** 将 byte[] 转换为 MultipartFile。** @param bytes       文件内容的字节数组* @param fieldName   表单字段的名称* @param fileName    文件名* @param contentType 内容类型* @return 转换后的 MultipartFile 对象* @throws IOException 如果转换过程中发生 I/O 错误*/public static MultipartFile convertByteToMultipartFile(byte[] bytes, String fieldName, String fileName, String contentType) throws IOException {if (bytes == null || bytes.length == 0) {throw new IllegalArgumentException("输入的字节数组不能为空");}FileItem item;try {DiskFileItemFactory factory = new DiskFileItemFactory();item = factory.createItem(fieldName, contentType, false, fileName);try (ByteArrayOutputStream bos = new ByteArrayOutputStream(bytes.length);OutputStream os = item.getOutputStream()) {bos.write(bytes);os.write(bos.toByteArray());}return new CommonsMultipartFile(item);} catch (IOException e) {throw new IOException("转换 byte[] 为 MultipartFile 时发生错误", e);}}/*** 从 URL 获取图片并转换为 MultipartFile。** @param imageUrl    图片的 URL* @param fileName    文件名* @param contentType 内容类型* @return 转换后的 MultipartFile 对象* @throws IOException 如果下载或转换过程中发生 I/O 错误*/public static MultipartFile convertUrlToMultipartFile(String fieldName, String imageUrl, String fileName, String contentType) throws IOException {if (StrUtil.isBlank(imageUrl)) {throw new IllegalArgumentException("URL 不能为空");}// 从 URL 下载图片byte[] bytes = HttpUtil.downloadBytes(imageUrl);return convertByteToMultipartFile(bytes, fieldName, fileName, contentType);}}

使用示例:

java">// 创建一个空byte 数组
byte[] bytes = new byte[1024];
// 示例 MultipartFile 对象
MultipartFile file = MultipartFileConverter.convertByteToMultipartFile(bytes, "image", "ok.jpg", "image/jpg");

MultipartFile转byte[]

java">MultipartFile file =  // 你的 MultipartFile 对象
// 直接获取 byte 数组
byte[] bytes = file.getBytes();

byte[]转InputStream

java">// 创建一个空byte 数组
byte[] bytes = new byte[1024];
ByteArrayInputStream bis = new ByteArrayInputStream(body);

InputStream转byte[]

java">// 使用 Hutool 的 IoUtil 读取 InputStream 并转换为 byte[]
byte[] bytes = IoUtil.readBytes(inputStream);

InputStream转MultipartFile

将 InputStream 转换为 byte[],然后请依照上面的教程 byte[]转MultipartFile 即可

java">ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
byte[] bytes = byteArrayOutputStream.toByteArray();

MultipartFile转InputStream

java">MultipartFile file =  // 你的 MultipartFile 对象
InputStream inputStream = file.getInputStream();

教程结束!

教程基本都来自己互联网各大博客帖子,此篇仅作记录方便日后使用!

参考资料

参考如下:

  • 将byte[]转换为MultipartFile
  • byte[]转MultipartFile、byte[]转File一次看个够 【 推荐!!!】
  • byte[]转换InputStream/MultipartFile

http://www.ppmy.cn/embedded/125641.html

相关文章

[k8s理论知识]1.runc容器管理工具

runc是一个用来启动和管理容器的轻量级工具&#xff0c;它是开放容器项目(Open Container Initiative, OCI)的标准实现。runc 是一个 CLI 工具&#xff0c;用于根据 OCI 规范创建和运行容器。它是容器底层的核心组件&#xff0c;负责直接与 Linux 内核的 cgroups 和 namespaces…

MinIO分片上传超大文件(纯服务端)

目录 一、MinIO快速搭建1.1、拉取docker镜像1.2、启动docker容器 二、分片上传大文件到MinIO2.1、添加依赖2.2、实现MinioClient2.3、实现分片上传2.3.0、初始化MinioClient2.3.1、准备分片上传2.3.2、分片并上传2.3.2.1、设置分片大小2.3.2.2、分片 2.3.3、分片合并 三、测试3…

python xml的读取和写入

import xml.etree.ElementTree as ET from xml.dom import minidom# 读取XML文档 tree ET.parse("./xml_3/z_20240827_001.xml") root tree.getroot() # 获取size元素 size_find_0 root.find("size") # 获取width子元素 size_w size_find_0.find("…

Cocos_鼠标滚轮放缩地图

文章目录 前言一、环境二、版本一_code2.分析类属性方法详细分析详细分析onLoad()onMouseWheel(event)详细分析 总结 前言 学习笔记&#xff0c;请多多斧正。 一、环境 通过精灵rect放置脚本实现鼠标滚轮放缩地图。 二、版本一_code import { _decorator, Component, Node }…

《Java程序员面试宝典》——(第二章节)

当前因为经济大环境不好、大厂裁员、就业情况差、企业要求变高、各行各业越来越卷&#xff0c;尤其是程序员&#xff0c;处于这个阶段&#xff0c;感觉特别明显&#xff01; 对于程序员这个群体来说&#xff0c;java程序员的占比就非常之高&#xff0c;就业市场等于说是千军万…

51单片机的无线通信智能车库门【proteus仿真+程序+报告+原理图+演示视频】

1、主要功能 该系统由AT89C51/STC89C52单片机LCD1602显示模块红外传感器光照传感器时钟模块步进电机蓝牙按键、LED、蜂鸣器等模块构成。适用于智能车库自动门、无线控制车库门等相似项目。 可实现功能: 1、LCD1602实时显示北京时间和自动/手动模式&#xff0c;以及验证是否成…

【线性代数】【第一章】行列式习题

文章目录 一. 重要定理1. 代数余子式2. 主要公式3. 方阵的行列式4. 克拉默法则 三. 典型例题1. 行列式计算1.1. 数字型行列式题型一&#xff1a;代数余子式降阶、拉普拉斯题型二&#xff1a;逐行消元&#xff0c;化为上三角式 1.2.抽象行列式&#xff08;考察行列式的性质&…

Leetcode203.移除链表元素-Python

题目链接&#xff1a;203. 移除链表元素 - 力扣&#xff08;LeetCode&#xff09; 题目描述&#xff1a; 给你一个链表的头节点 head 和一个整数 val &#xff0c;请你删除链表中所有满足 Node.val val 的节点&#xff0c;并返回 新的头节点 。 示例 1&#xff1a; 输入&a…