通过图片和base64互转,实现图片上传和下载显示以及踩坑

前言

实现一个类似于微信的图片界面,包括拍照和相册,拍照包括裁剪,相册包括预览,可以选中指定张数的图片,将图片转换为base64上传到服务器。可以从服务器将已经上传的图片资源,通过base64字符串下载,然后将base64转换为图片,在界面显示,具体效果图:
在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述
这个类似于微信的效果,是我在网上找了个demo,这篇文章主要是谈base64和图片转换遇到的问题,实现效果可以自行下载这个demo,仿微信选择图片demo,(不知道为什么,现在csdn上传资源,不能设置分数,直接默认5积分,本来想免费分享的,因为这个demo是别人的成果。。。)可以自行修改需要的样式。涉及到图片上传,就会涉及到图片压缩,网上现在流行的几种压缩库,本来使用的是Compressor,发现压缩.png文件会报错,问题可以查看这个ExifInterface got an unsupported image,最后用了LuBan,Curzibn/Luban

实现

首先,根据以上demo,可以对照片进行处理,相册可以预览,拍照可以裁剪,现在就进行对图片处理
先贴几个对图片进行处理的方法

 /**
     * 将文件中的数据读取到Byte数组中
     *
     * @param file
     * @return
     */
    public static byte[] readRealFileToByte(File file)
    {
        Long filelength = file.length(); // 获取文件长度

        byte[] filecontent = new byte[filelength.intValue()];

        try
        {
            FileInputStream in = new FileInputStream(file);
            in.read(filecontent);
            in.close();
        }
        catch (FileNotFoundException e)
        {
            e.printStackTrace();
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }


        return filecontent; // 返回文件内容,默认编码
    }

    public static String getEncoded64ImageStringFromByte(byte[] bytes)
    {
        if (bytes == null || bytes.length == 0)
            return null;

        String imgString = Base64.encodeToString(bytes, Base64.DEFAULT);

        return imgString;
    }

    /**
     * base64字符串转化成图片
     */
    public static String GenerateImage(String base64Code, String savePathKey)
    {

        //对字节数组字符串进行Base64解码并生成图片
        if (base64Code == null)
        { //图像数据为空
            return "";
        }

        try
        {
            byte[] buffer = Base64.decode(base64Code, Base64.DEFAULT);

            // 新生成的jpg图片
            // 新图片的文件夹, 如果没有, 就创建

            File fileDir = new File(dirPath);
            if (!fileDir.exists())
            {
                fileDir.mkdirs();
            }
            // 文件夹现在存在了, 可以在此文件夹下创建图片了
            String imgFilePath = dirPath + savePathKey + ".jpg";
            File file = new File(imgFilePath);
            if (!file.exists())
            {
                file.createNewFile();
            }
            OutputStream out = new FileOutputStream(imgFilePath);
            out.write(buffer);
            out.flush();
            out.close();
            return imgFilePath;
        }
        catch (Exception e)
        {
            return "";
        }
    }

    /**
     * 删除指定目录下文件及目录

     */
    public static void deleteFolderFile(String filePath)
    {
        if (!TextUtils.isEmpty(filePath))
        {
            try
            {
                File file = new File(filePath);
                if (file.isDirectory())
                {// 处理目录
                    File files[] = file.listFiles();
                    for (int i = 0; i < files.length; i++)
                    {
                        deleteFolderFile(files[i].getAbsolutePath());
                    }
                }

                if (!file.isDirectory())
                {// 如果是文件,删除
                    file.delete();
                }
                else
                {// 目录
                    if (file.listFiles().length == 0)
                    {// 目录下没有文件或者目录,删除
                        file.delete();
                    }
                }

            }
            catch (Exception e)
            {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
 public static Bitmap imageZoom(Bitmap bitMap,double maxSize) {
        //图片允许最大空间   单位:KB
        //将bitmap放至数组中,意在bitmap的大小(与实际读取的原文件要大)
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        bitMap.compress(Bitmap.CompressFormat.JPEG, 100, baos);
        byte[] b = baos.toByteArray();
        //将字节换成KB
        double mid = b.length/1024;
        //判断bitmap占用空间是否大于允许最大空间  如果大于则压缩 小于则不压缩
        if (mid > maxSize) {
            //获取bitmap大小 是允许最大大小的多少倍
            double i = mid / maxSize;
            //开始压缩  此处用到平方根 将宽带和高度压缩掉对应的平方根倍 (1.保持刻度和高度和原bitmap比率一致,压缩后也达到了最大大小占用空间的大小)
            bitMap = zoomImage(bitMap, bitMap.getWidth() / Math.sqrt(i),
                    bitMap.getHeight() / Math.sqrt(i));
        }
        return bitMap;
    }



    /***
     * 图片的缩放方法
     *
     * @param bgimage
     *            :源图片资源
     * @param newWidth
     *            :缩放后宽度
     * @param newHeight
     *            :缩放后高度
     * @return
     */
    private static Bitmap zoomImage(Bitmap bgimage, double newWidth,
                                   double newHeight) {
        // 获取这个图片的宽和高
        float width = bgimage.getWidth();
        float height = bgimage.getHeight();
        // 创建操作图片用的matrix对象
        Matrix matrix = new Matrix();
        // 计算宽高缩放率
        float scaleWidth = ((float) newWidth) / width;
        float scaleHeight = ((float) newHeight) / height;
        // 缩放图片动作
        matrix.postScale(scaleWidth, scaleHeight);
        Bitmap bitmap = Bitmap.createBitmap(bgimage, 0, 0, (int) width,
                (int) height, matrix, true);
        return bitmap;
    }

图片上传
首先,对已经选择的图片,进行压缩,然后转换为base64,调用接口上传,具体代码

private ArrayList<ImageItem> selImageList; //当前选择的所有图片
......
 private void processSubmit()
    {
        showProgressDialog("请稍候", null, null);
        //selImageList已选中图片列表
        if (selImageList.size() > 0)
        {

            List<File> selImageFileList = new ArrayList<>();
            ArrayList<String> base64List = new ArrayList<>();
            for (ImageItem imageItem : selImageList)
            {

                try
                {
                    File imageFile = new File(imageItem.path);
                    selImageFileList.add(imageFile);
                }
                catch (Exception e)
                {
                    e.printStackTrace();
                }
            }

            Luban.with(this).load(selImageFileList).ignoreBy(100).setCompressListener(new OnCompressListener()
            {
                @Override
                public void onStart()
                {
                    // TODO 压缩开始前调用,可以在方法内启动 loading UI
                }

                @Override
                public void onSuccess(File file)
                {
         /*

                    String filetype = "";
                    Log.e("file length", ((float) imageFile.length() / 1024 / 1024) + "MB");
*/

//有些情况下,发现Luban压缩不到指定尺寸,可以手动写一个方法,算是个备选方案
                    Bitmap bitmap = ImageUtil.imageZoom(BitmapFactory.decodeFile(file.getPath()),50);
                    String base64 = "";

                    if (bitmap == null)
                    {
                        ToastUtil.showShortToast("获取图片失败");
                        return;
                    }
                    Log.e("bitmap size", ((float) (bitmap.getRowBytes() * bitmap.getHeight()) / 1024 / 1024) + "MB");

                    base64 = ImageUtil.bitmaptoString(bitmap);

                /*    // TODO 压缩成功后调用,返回压缩后的图片文件
                    byte[] bytes = ImageUtil.readRealFileToByte(file);
                    Log.i("MyImageTest", "After Compress bytes.length=  " + ((float) bytes.length / 1024 / 1024) + "MB");
                    String base64 = ImageUtil.getEncoded64ImageStringFromByte(bytes);*/
   
                    base64List.add(base64);
                    if (base64List.size() == selImageList.size())
                    {
                        uploadImg(base64List);
                    }

                }

                @Override
                public void onError(Throwable e)
                {
                    // TODO 当压缩过程出现问题时调用
                    dismissProgressDialog();
                    ToastUtil.showShortToast("图片转换失败");
                }
            }).launch();

        }
        else
        {
            uploadImg(null);
        }
    }

上传很简单,选择图片–>压缩–>转成base64–>调用接口上传
下载图片
下载图片的流程:调用接口–>获取base64–>转换为图片–>进行显示
首先,为了配合上边说的仿微信的demo,尽量少改动,我这边是将base64转成的图片,保存到sd卡中的一个文件夹下,然后在上传图片结束以后,将该文件夹删除。保存到本地以后,就可以跟其他本地图片一样操作了。
踩坑
重点重点:就这个把base64转成图片,保存到本地,然后显示的这样一个功能,有一个很大的坑,因为demo里边显示图片用的是Glide,我保存图片是保存到sd卡,所以,坑就是:如果我下载下来的图片,命名为1.jpg,然后更改过图片,重新上传,然后重新下载,保存到本地,也是被命名为1.jpg,本地sd卡中的图片已经更改了,但是,Glide的缓存机制,在这里插入图片描述
具体参看Glide v4文档
所以,Glide会先去缓存中获取图片,现在缓存中是有1.jpg的,所以根本不会去sd卡里获取,所以显示的还是老图片,这样就是一个问题,因为其他功能也在使用Glide,所以不敢修改Glide的缓存配置,现在的方法就是,将每次下载的base64转换成的图片的命名,设置为唯一的,这样内存中就不会有相同姓名的图片,只能到sd卡里读取,我这边命名是根据时间加索引,本来只是时间,结果有可能同一秒内可以处理多张图片,也会出现问题,所以加上索引就是唯一了
下载图片实现

  if (data.getRstData().size() > 0)
                    {
                        //图片实体类
                        uploadImgBean = data.getRstData().get(0);
                        //多个图片通过§字符分割
                        if (uploadImgBean.getYWIMG().contains("§"))
                        {
                            String[] base64s = uploadImgBean.getYWIMG().split("§");
                            for (int i = 0; i < base64s.length; i++)
                            {
                                //将接口获取的图片临时保存到本地,命名必须唯一,如果缓存中有相同名称的图片,会优先获取缓存中的,不会度本地sd中的,容易造成显示不正确的bug
                                ImageItem imageItem = new ImageItem();
                                imageItem.path = ImageUtil.GenerateImage(base64s[i], DateUtil.getNowDateHHmmssString() + i);
                                selImageList.add(imageItem);
                            }
                        }
                        else
                        {
                            if(!uploadImgBean.getYWIMG().equals("")){
                                ImageItem imageItem = new ImageItem();
                                imageItem.path = ImageUtil.GenerateImage(uploadImgBean.getYWIMG(), DateUtil.getNowDateHHmmssString() + "10");
                                selImageList.add(imageItem);
                            }

                        }
                        adapter.setImages(selImageList);
                        // adapter.notifyDataSetChanged();
                        isButtonCanClick(false);
                        dismissProgressDialog();
  @Override
    protected void onDestroy()
    {
        super.onDestroy();
        try
        {
            ImageUtil.deleteFolderFile(ImageUtil.dirPath);
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
    }
/**
     * 获得当前时间
     * <p/>
     * 格式:10:38:53
     *
     * @return
     */
    public static String getNowDateHHmmssString()
    {
        SimpleDateFormat formatter = new SimpleDateFormat("HH:mm:ss");
        Date currentTime = new Date();
        String dateString = formatter.format(currentTime);
        return dateString;
    }

  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
下面是Java实现Base64图片互转的代码: ```java import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.InputStream; import java.io.OutputStream; import java.util.Base64; public class ImageBase64Util { /** * 将图片转换成Base64编码字符串 * * @param imagePath 图片路径 * @return Base64编码字符串 */ public static String imageToBase64(String imagePath) { String base64 = null; InputStream in = null; try { in = new FileInputStream(imagePath); byte[] bytes = new byte[in.available()]; in.read(bytes); base64 = Base64.getEncoder().encodeToString(bytes); } catch (Exception e) { e.printStackTrace(); } finally { try { in.close(); } catch (Exception e) { e.printStackTrace(); } } return base64; } /** * 将Base64编码字符串转换成图片 * * @param base64 Base64编码字符串 * @param imagePath 图片保存路径 * @return 是否保存成功 */ public static boolean base64ToImage(String base64, String imagePath) { boolean result = false; OutputStream out = null; try { out = new FileOutputStream(imagePath); byte[] bytes = Base64.getDecoder().decode(base64); out.write(bytes); result = true; } catch (Exception e) { e.printStackTrace(); } finally { try { out.close(); } catch (Exception e) { e.printStackTrace(); } } return result; } } ``` 使用示例: ```java public class ImageBase64UtilTest { public static void main(String[] args) { // 图片路径 String imagePath = "D:/test.png"; // 将图片转换成Base64编码字符串 String base64 = ImageBase64Util.imageToBase64(imagePath); System.out.println(base64); // 将Base64编码字符串转换成图片 String newImagePath = "D:/newtest.png"; ImageBase64Util.base64ToImage(base64, newImagePath); } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值