通過圖片和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();
            }
        }
    }

圖片上傳
首先,對已經選擇的圖片,進行壓縮,然後轉換爲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)
                {
                    // 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;
    }

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章