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

news2024/11/24 7:51:54

前言

问题产生:

最近开发项目的时候,遇到了文件上传对象转换的问题 -> 我在对接抖音开放平台的时候,有一个图片上传的接口,需要将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时,可以通过以下步骤完成:

  1. 导入相关依赖:确保项目中引入了Spring框架的相关依赖,以及文件上传所需的其他依赖。
  2. 创建一个实现了MultipartFile接口的自定义类:由于MultipartFile是一个接口,无法直接实例化,因此需要创建一个自定义类来实现该接口。
  3. 实现自定义类的相关方法:自定义类需要实现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 的请求下载工具来获取 urlbyte 字节数据 。

// 图片的链接
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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2203742.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

无人机之巡航控制篇

一、巡航控制的基本原理 无人机巡航控制的基本原理是通过传感器检测无人机的飞行状态和环境信息&#xff0c;并将其反馈给控制器。控制器根据反馈信息和任务需求&#xff0c;计算出无人机的控制指令&#xff0c;并将其发送给执行机构。执行机构根据控制器的控制指令&#xff0c…

数据结构与算法——Java实现 33.堆排序

刻意去找的东西&#xff0c;往往是找不到的。 天下万物的来和去&#xff0c;都有它的时间。 —— 24.10.10 使用堆进行排序 算法描述 1.heapify 建立大顶堆&#xff08;所有结点的父元素大于子元素&#xff09; 2.将堆顶与堆底交换(最大元素被交换到堆底)&#xff0c;缩小并…

Python测试框架--Allure

严格意义上讲 Allure 不算是测试框架&#xff0c;但是它是生成漂亮测试报告的开源工具&#xff0c;搭配 Pytest 测试框架食用更搭。 也就是说 Allure 是在 Pytest 执行完生成的测试数据的基础上&#xff0c;对测试数据进行处理统计&#xff0c;生成格式统一、美观的测试报告。 …

java反射Class类的api

目录 Class类 1.构造器相关 1&#xff09;Constructor getConstructor(Class... parameterTypes): 2&#xff09;Constructor[] getConstructors(): 3&#xff09;Constructor getDeclaredConstructor(Class... parameterTypes): 4&#xff09;Constructor[] getDeclaredConstr…

国外火出圈儿的PM御用AI编程工具Bolt.new效果干不过国产的CodeFlying?号称全新定义全栈开发流程?

不知道大家最近有没有发现国外的很多AI都在挤破脑袋想去提升大模型的编程能力&#xff0c; 离我们最近的是上周Openai 发布的全新模型GPT-4o-Canvas&#xff0c; 拥有超强的代码编写能力。 另外还有LlamaCoder、Cursor、Claude artifacts、Replit... 光是今年一年就推出了好…

深入理解Transformer的笔记记录(精简版本)---- Transformer

自注意力机制开启大规模预训练时代 1 从机器翻译模型举例 1.1把编码器和解码器联合起来看待的话,则整个流程就是(如下图从左至右所示): 1.首先,从编码器输入的句子会先经过一个自注意力层(即self-attention),它会帮助编码器在对每个单词编码时关注输入句子中的的其他单…

xtu oj 四位数

样例输入# 2 1990 1111样例输出# 5 0 分离整数与合并 AC代码 #include<stdio.h> //判断四个数码是否相等 int Judge(int n){int flag1;int gn%10,sn/10%10,bn/100%10,qn/1000;if(gs&&gb&&gq)flag0;return flag; } int main(){int T;scanf("%d…

AI无人直播风口,云微客替你抓住了

现在很多老板都在找风口&#xff0c;其实风口就是我们大家看得见、摸得着的东西&#xff0c;就比如说近期比较火热的直播带货就属于是一个风口。可能很多老板会说直播、短视频之类的风口过于高科技&#xff0c;搞不来也搞不懂&#xff0c;跟自己好像没太大关系。 其实不然&…

嵌入式开发:STM32 硬件 CRC 使用

测试平台&#xff1a;STM32G474系列 STM32硬件的CRC不占用MCU的资源&#xff0c;计算速度快。由于硬件CRC需要配置一些选项&#xff0c;配置不对就会导致计算结果错误&#xff0c;导致使用上没有软件计算CRC方便。但硬件CRC更快的速度在一些有时间资源要求的场合还是非…

【二叉搜索树】1 leetcode 98 验证二叉搜索树

1 题目描述 题目链接&#xff1a;验证二叉搜索树 2 题目解析 搜索二叉树的特点就是&#xff0c; 中序遍历之后的值是有序的。 根据这个性质&#xff0c;可以对树进行中序遍历&#xff0c;将遍历的结果存入到vector中最后判断vector中的值的顺序。如果是从小到大有序的&…

变色龙算法(CSA)的MATLAB代码复现

目录 1 变色龙算法优化BP神经网络代码复现 2 变色龙算法优化支持向量机代码复现 3 变色龙算法优化长短期记忆神经网络代码复现 1 变色龙算法优化BP神经网络代码复现 1&#xff09;单输出回归预测&#xff1a;单输出回归预测&#xff1a;变色龙算法优化BP神经网络模型&#…

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…

Windows命令行执行cmake

生成Win32 工程并编译 cmake ../../ -G "Visual Studio 16 2019" -A Win32set pathC:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Current\Bin;%path%msbuild VideoNetOptimization.sln /p:ConfigurationRelWithDebInfo /p:PlatformWin3…

MSYS2+GCC 安装与应用保姆手册

msys2 提供可在Windows下使用 GCC 编译器&#xff1b;并且&#xff0c;借助 Linux 包管理功能&#xff0c;可轻松下载丰富的可在Windows下直接使用的 C/C 开发包&#xff0c;包括编译好的二进制包。 网络库asio、准标准库boost、zip解压缩、json格式处理、引擎 SDL……十八般兵…

2025年软考高项(信息系统项目管理师)包过班靠谱吗?

随着新一轮软考的到来&#xff0c;出现了很多“软考包过”的信息&#xff0c;但需要注意的是&#xff0c;“软考包过”根本不可信&#xff01; 因此不要想着依靠不正当手段来取得证书&#xff0c;要知道&#xff0c;如今 软考 由笔试变为机考&#xff0c;很难作弊&#xff0c;…

​速卖通、敦煌卖家备战双11+黑五前,怎么增加店铺曝光?

在速卖通这个竞争激烈的跨境电商平台上&#xff0c;店铺曝光率是决定销售成败的关键因素之一。为了在众多商家中脱颖而出&#xff0c;增加速卖通店铺曝光显得尤为重要。速卖通怎么增加店铺曝光&#xff1f; 一、速卖通怎么增加店铺曝光? 1、优化产品列表 速卖通的产品列表是…

需求9——通过一个小需求来体会service层的作用

昨天在完成了睿哥的需求验收之后&#xff0c;暂时没有其他任务&#xff0c;因此今天可能会比较有空闲时间。趁着这个机会&#xff0c;我打算把之前完成的一些需求进行总结&#xff0c;方便以后复习和参考。 在8月份的时候&#xff0c;我负责了一个需求&#xff0c;该需求的具体…

白色简洁大方公司企业网站源码 WordPress主题2款

WordPress白色简洁大方公司企业网站主题2款 白色整洁风格wordpress主题是一款比较新颖的国际设计范风格 简洁而大方的 WordPress 主题&#xff0c;适合个人博客、企业和工作室用。 完美支持下拉菜单的wordpress企业主题。 wordpress简白企业模板是一款适合企业站以及工作室…

c++基础知识复习(1)

前期知识准备 1 构造函数 &#xff08;1&#xff09;默认构造函数&#xff1a;没有参数传入&#xff0c;也没有在类里面声明 &#xff08;2&#xff09;手动定义默认构造函数&#xff1a;没有参数传入&#xff0c;但是在类里面进行了声明 可以在类外实现或者类内实现 以下案…

IDM6.42下载器最新版本,提速你的网络生活!

&#x1f680;【速度与激情&#xff0c;IDM 6.42来袭&#xff01;】&#x1f4a3; Hey, 亲爱的下载达人们&#xff01;&#x1f44b; 今天我要给你们安利一个神器——Internet Download Manager&#xff08;简称IDM&#xff09;&#xff0c;版本6.42&#xff0c;这可不是普通的…