前言
问题产生:
最近开发项目的时候,遇到了文件上传对象转换的问题 -> 我在对接抖音开放平台的时候,有一个图片上传的接口,需要将byte[]
转为MultipartFile
对象,但是发现根本没有这样的工具类,后面翻阅了不少帖子得到了解决方案。
需求是这样的:
阿里云的OSS
对象存储照片URL
需要通过Java
获取到图片流,不过我这里下载的是byte
数组,byte
数组与流对象之间的转换还是比较简单的,这里我就是希望 byte
数组转为MultipartFile
对象:
获取图片的字节数据:
// 图片的链接
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
时,可以通过以下步骤完成:
- 导入相关依赖:确保项目中引入了Spring框架的相关依赖,以及文件上传所需的其他依赖。
- 创建一个实现了
MultipartFile
接口的自定义类:由于MultipartFile
是一个接口,无法直接实例化,因此需要创建一个自定义类来实现该接口。 - 实现自定义类的相关方法:自定义类需要实现
MultipartFile
接口中的方法,包括getName()
、getOriginalFilename()
、getContentType()
、getSize()
、getInputStream()
等方法。其中,getInputStream()
方法需要将byte[]
转换为InputStream
。
以下是一个示例代码,展示了如何将byte[]
转换为MultipartFile
:
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 的名称
*/
@Override
public String getName() {
return name;
}
/**
* 获取原始文件名。
*
* @return 原始文件名
*/
@Override
public String getOriginalFilename() {
return originalFilename;
}
/**
* 获取文件的内容类型。
*
* @return 文件的内容类型
*/
@Override
public String getContentType() {
return contentType;
}
/**
* 判断 MultipartFile 是否为空。
*
* @return 如果字节数组长度为 0,则返回 true;否则返回 false
*/
@Override
public boolean isEmpty() {
return this.content.length == 0;
}
/**
* 获取文件的大小(字节数)。
*
* @return 文件的大小
*/
@Override
public long getSize() {
return this.content.length;
}
/**
* 获取字节数组内容。
*
* @return 字节数组内容
* @throws IOException 如果发生 I/O 错误
*/
@Override
public byte[] getBytes() throws IOException {
return this.content;
}
/**
* 获取输入流。
*
* @return 输入流
* @throws IOException 如果发生 I/O 错误
*/
@Override
public InputStream getInputStream() throws IOException {
return new ByteArrayInputStream(this.content);
}
/**
* 将文件内容保存到指定位置。
*
* @param dest 目标文件路径
* @throws IOException 如果发生 I/O 错误
* @throws IllegalStateException 如果文件状态不正确
*/
@Override
public void transferTo(File dest) throws IOException, IllegalStateException {
// 实现文件的保存操作,可根据具体需求自行实现
// 例如:Files.write(dest.toPath(), content);
Files.write(dest.toPath(), content);
}
}
使用该自定义类时,可以通过以下方式将byte[]
转换为MultipartFile
:
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
的请求下载工具来获取 url
的 byte
字节数据 。
// 图片的链接
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
最后一个/
反斜杠后面内容的工具类:
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 {
// 解析 URL
java.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) {
// 示例 URL
String 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:
这里封转了一个工具类发起请求可以直接进行从响应头里面获取响应内容类型:
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();
// 检查响应状态码是否为 200
if (response.getStatus() == 200) {
// 从响应头中获取 ContentType
return response.header("Content-Type");
} else {
System.err.println("无法获取资源,状态码: " + response.getStatus());
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public static void main(String[] args) {
// 示例 URL
String imageUrl = "https://xx.com?test.jpg"; // 替换为你实际的图片 URL
// 获取 ContentType
String 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>
工具代码如下:
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);
}
}
使用示例:
// 创建一个空byte 数组
byte[] bytes = new byte[1024];
// 示例 MultipartFile 对象
MultipartFile file = MultipartFileConverter.convertByteToMultipartFile(bytes, "image", "ok.jpg", "image/jpg");
MultipartFile转byte[]
MultipartFile file = // 你的 MultipartFile 对象
// 直接获取 byte 数组
byte[] bytes = file.getBytes();
byte[]转InputStream
// 创建一个空byte 数组
byte[] bytes = new byte[1024];
ByteArrayInputStream bis = new ByteArrayInputStream(body);
InputStream转byte[]
// 使用 Hutool 的 IoUtil 读取 InputStream 并转换为 byte[]
byte[] bytes = IoUtil.readBytes(inputStream);
InputStream转MultipartFile
将 InputStream 转换为 byte[],然后请依照上面的教程 byte[]转MultipartFile 即可
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
byte[] bytes = byteArrayOutputStream.toByteArray();
MultipartFile转InputStream
MultipartFile file = // 你的 MultipartFile 对象
InputStream inputStream = file.getInputStream();
教程结束!
教程基本都来自己互联网各大博客帖子,此篇仅作记录方便日后使用!
参考资料
参考如下:
- 将byte[]转换为MultipartFile
- byte[]转MultipartFile、byte[]转File一次看个够 【 推荐!!!】
- byte[]转换InputStream/MultipartFile