IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> Java 下载url图片 二进制图片转MultipartFile文件上传图片 -> 正文阅读

[移动开发]Java 下载url图片 二进制图片转MultipartFile文件上传图片

HttpUrlConnection 基础使用
IOUtils快速进行内容复制与常用方法

附一个方便查看图片base64的工具网址:图片base64互转工具

下载图片

在项目中有需求是将当前上传Amazon S3上的图片在发送一份至其他系统中保存,对方提供的接口是传图片base64和图片名称,上传成功后返回一个图片id。而库中取到的是上传s3后拿到的url。通过url先获取图片二进制,再base64发送给对方。
通过url下载图片,获得图片二进制数据代码如下:

import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Base64;

public class PictureUtil {
	//获取图片二进制
    private byte[] downloadPicture(String url){
        URL urlConnection = null;
        HttpURLConnection httpURLConnection = null;
        try {
            urlConnection = new URL(url);
            httpURLConnection = (HttpURLConnection) urlConnection.openConnection();
            InputStream in = httpURLConnection.getInputStream();
            byte[] buffer = new byte[1024];
            int len = 0;
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            while ((len = in.read(buffer)) != -1){
                out.write(buffer,0,len);
            }
            in.close();
            out.close();
            return out.toByteArray();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            httpURLConnection.disconnect();
        }
        return null;
    }

    public String getPictureBase64(String url){
    	//base64编码
        return Base64.getEncoder().encodeToString(downloadPicture(url));
    }
}

通过url获取图片,实际上就是发送一个http的get请求,因此使用HttpURLConnection的默认参数就可达到目的。如果发送其他请求,可以对其设置请求头或响应头等。

一个支持HTTP特定功能的URLConnection。

使用这个类遵循以下模式:
  1.通过调用URL.openConnection()来获得一个新的HttpURLConnection对象,并且将其结果强制转换为HttpURLConnection.
  2.准备请求。一个请求主要的参数是它的URI。请求头可能也包含元数据,例如证书,首选数据类型和会话cookies.
  3.可以选择性的上传一个请求体。HttpURLConnection实例必须设置setDoOutput(true),如果它包含一个请求体。通过将数据写入一个由getOutStream()返回的输出流来传输数据。
  4.读取响应。响应头通常包含元数据例如响应体的内容类型和长度,修改日期和会话cookies。响应体可以被由getInputStream返回的输入流读取。如果响应没有响应体,则该方法会返回一个空的流。
  5.关闭连接。一旦一个响应体已经被阅读后,HttpURLConnection 对象应该通过调用disconnect()关闭。断开连接会释放被一个connection占有的资源,这样它们就能被关闭或再次使用。

注意:之前有使用InputStream.available()方法获取可读字节的长度,以new相同长度的byte数组读近byte直接返回,代码如下:

    private byte[] downloadPicture(String url){
        URL urlConnection = null;
        HttpURLConnection httpURLConnection = null;
        try {
            urlConnection = new URL(url);
            httpURLConnection = (HttpURLConnection) urlConnection.openConnection();
            InputStream in = httpURLConnection.getInputStream();
            //使用available()方法获取数据长度
            byte[] data = new byte[in.available()];
            in.read(data);
            in.close();
            return data;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            httpURLConnection.disconnect();
        }
        return null;
    }

在本地测试时,试过两三次,都可以拿到图片完整的流数据,但是到部署到uat环境上时,通过打印日志发现有时候能拿到图片数据,有时候拿不到数据。
原因正是available使用的不对,InputStream.available()读取文件流长度不不完整,该方法读取本地文件时一般不会出现问题,但是通过网路传输就会出现图片传输不完整的情况,因为网络通讯是间断性的一串字节往往分几批进行发送。
通过查看InputStream.available()源码的注释,也发现注释也说明了该问题:

 * 返回可以从此输入流读取(或跳过)的字节数的估计值,而不会因下一次调用此输入流的方法而阻塞。下一次调用可能是同一个线程或另一个线程。单次读取或跳过这么多字节不会阻塞,但可能读取或跳过更少的字节。
 
 * 请注意,虽然 {@code InputStream} 的某些实现会返回流中的总字节数,但很多不会。使用此方法的返回值来分配用于保存此流中所有数据的缓冲区永远是不正确的。

 * 如果此输入流已通过调用 {@link #close()} 方法关闭,则此方法的子类实现可能会选择抛出 {@link IOException}。

 * {@code InputStream} 类的 {@code available} 方法总是返回 {@code 0}。

 * 这个方法应该被子类覆盖。

因此不应使用该方法企图获取正确的文件的数据长度。

上传图片

当获取该图片时,通过对方返回的图片id,调用他们的获取图片接口,将返回图片base64的数据。将图片还原成MultipartFile上传至Amazon S3。将MultipartFile上传至S3是项目中已有的方法和功能,此处只需拿到图片二进制数据,转换为MultipartFile文件,调用已有方法即可。
通过搜索资料得知MockMultipartFile可以转换,代码如下:

import org.springframework.mock.web.MockMultipartFile;
import org.springframework.web.multipart.MultipartFile;
import java.util.Base64;

public class PictureUtil {
    public MultipartFile getFileByPictureBase64(String name, String pic) {
        byte[] buffer = Base64.getDecoder().decode(pic);
        return getMultipartFile(name, buffer);
    }

    //二进制->MultipartFile
    private MultipartFile getMultipartFile(String name, byte[] bytes) {
        MockMultipartFile mockMultipartFile = null;
        try {
            mockMultipartFile = new MockMultipartFile("name", bytes);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return mockMultipartFile;
    }
}

但是MockMultipartFile是在spring-test包下,要加依赖。此外该包是单元测试时使用的,不适合在业务中使用。因此改用其他方法。
通过FileItem对象,使用CommonsMultipartFile类获取MultipartFile,MultipartFile 是接口, CommonsMultipartFile 是其实现类。代码如下:

import org.apache.commons.compress.utils.IOUtils;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileItemFactory;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;

public class PictureUtil {

    //二进制文件转换MultipartFile
    public MultipartFile getMultipartFile(String name, byte[] bytes) {
        MultipartFile mfile = null;
        ByteArrayInputStream in = null;
        try {
            in = new ByteArrayInputStream(bytes);
            FileItemFactory factory = new DiskFileItemFactory(16, null);
            FileItem fileItem = factory.createItem("mainFile", "text/plain", false, name);
            IOUtils.copy(new ByteArrayInputStream(bytes), fileItem.getOutputStream());
            mfile = new CommonsMultipartFile(fileItem);
            in.close();
        }catch (Exception e){
            e.printStackTrace();
        }
        return mfile;
    }
}

通过ByteArrayInputStream字节流读入bytes数组,使用IOUtils类的方法直接拷贝到新建的FileItem对象的输出流中,使用FileItem对象构造CommonsMultipartFile,从而得到MultipartFile。

总结

本文讲述了项目实际过程中遇到的两个问题的解决方法:

  1. 通过url获取图片二进制:使用URL和HttpURLConnection发送http请求获取图片数据流,使用循环读取数据方式读取,不建议使用available()判断数据长度;
  2. 通过二进制获取MultipartFile:使用FileItemFactory创建一个FileItem来构造CommonsMultipartFile,不建议使用spring-test包下MockMultipartFile。
  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2021-11-30 15:43:53  更:2021-11-30 15:45:53 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 5:27:03-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码