【问题标题】:Process to compress an image for uploading to a server in Android压缩图像以上传到 Android 中的服务器的过程
【发布时间】:2026-02-15 03:25:02
【问题描述】:

我想在将图像上传到服务器之前编写自己的代码来压缩图像。

我在 SO 上看到了很多帖子,例如 this,还有很多其他帖子,但到处只有代码示例。它实际上并没有在任何地方解释。我无法从给定的代码中理解。

我只是想了解压缩图像的整体方法,以便我可以开始自己编写代码。

我不是在寻找代码,我只是在寻找一个人压缩图像需要遵循的步骤。 (就像在编写程序之前编写算法,即伪代码)

【问题讨论】:

  • 查看this 了解图像压缩。要了解作为初学者的压缩知识,请查看LZW Compression

标签: java android image-compression


【解决方案1】:

我认为 Chetan Joshi 的答案是迄今为止最接近的。但为了进一步解释,

“压缩”图像和“缩放”(调整大小)图像是不同的。由于您只是想在上传到服务器时减少网络带宽 (kb/Mb),因此您很可能会谈论“缩放”,因为这将对图像的大小产生最大的影响。

注意,与其他 Android 答案一样,您需要使用 Bitmaps。您需要使用与位图关联的 API 调用来做您想做的事。

起点:

如果您允许您的应用使用相机拍照或从图库中选择照片,您将获得一个 Uri,它允许您像这样打开 InputStream:

InputStream is = getContext().getContentResolver().openInputStream(photoUri);

这是第一步。下一部分是你想要这样的伪代码:

  1. 获取照片/所选图片的InputStream。这只是代表图像的字节流。
  2. 使用BitmapFactory.decodeStream(InputStream src) 将流解码为Bitmap
  3. 使用Bitmap.createScaledBitmap(src, dstWidth, dstHeight, filter); 缩放(调整大小)位图。您必须自己决定目标宽度和目标高度。请参阅下一组伪代码。
  4. 使用imageFormat.getBitmapCompressFormat(), 100, output) 压缩位图,其中输出是ByteArrayOutputStream 的一个实例
  5. 您现在可以调用output.toByteArray(); 来获取字节。
  6. 字节是您要发送到服务器的内容。

注意如何选择要缩放到的目标宽度和高度:

  1. 确定最大高度和最大宽度,例如 240 x 240。您将把它与位图的实际高度和实际宽度进行比较。
  2. 如果太高(实际高度 > 最大高度),则计算比例因子,即最大高度/实际高度
  3. 如果太宽(实际宽度 > 最大宽度),则计算一个缩放因子,即最大宽度/实际宽度
  4. 您的实际比例因子是这两个比例因子中的最小值
  5. 返回实际高度 * 缩放系数和实际宽度 * 缩放系数

总体而言,高级流程是这样的:

  1. 打开图像字节流
  2. 将字节转换为位图
  3. 缩放位图
  4. 压缩位图
  5. 将位图转换回字节

【讨论】:

    【解决方案2】:

    Android 中的图像压缩意味着我们正在使用位图,当我们将任何大图像压缩到更低时,我们在位图类中有方法 compress 但是如果原始位图和想要的位图的大小之间的差异太大,那么位图质量总是会降低.

    要压缩高质量的位图,我们必须使用递归过程来压缩任何位图,而不是将任何位图直接从原始高度宽度压缩到想要的高度和宽度,我们应该用 3 到 5 个步骤压缩图像,例如,如果我们的图像大小是1000x1000 和想要的宽度和高度是 300x300 然后我们应用递归过程来执行此操作,如下所示:

    1000 x 1000
    
    850 x 850
    
    650 x 650
    
    300 x 300 
    

    这样我们的位图质量不会降低,但请确保不要每次使用相同的位图对象分配压缩位图时都创建新的位图对象,否则您必须面临 OutOfmemmory 问题。

    【讨论】:

      【解决方案3】:

      您可以只使用 android sdk - bitmap.compress();

      ByteArrayOutputStream bos = new ByteArrayOutputStream();
      
      if (myBitmap.compress(Bitmap.CompressFormat.JPEG, 100, bos)) {
          //image is now compressed into the output stream
          uploadImage(bos);
      } else {
          //compress failed
      }
      

      您可以随时查看 android 源代码以了解更多信息,但如果您是一名 android 开发人员,我认为花时间学习如何压缩不应该是您的主要关注点,特别是当您拥有如此简单的解决方案时,如果你仍然认为你会这样做,我会先学习如何压缩文件,然后我会转到图像,然后我才会查看源代码

      【讨论】:

        【解决方案4】:

        由于您实际上并不是在寻找代码,因此这里是了解图像压缩背后的思考过程的绝佳资源Data Compression Theory

        有多种算法利用不同的技术来利用所涉及的不同变量。计算复杂性、存储空间、压缩比和保持图像质量都是这些不同算法(或文件格式)寻求优化的因素,通常偏爱一个属性而不是另一个属性。例如,PNG 与 JPEG 的质量与空间

        【讨论】:

          【解决方案5】:

          Java 提供了ImageIO API,可以帮助您根据您选择的质量和压缩系数来压缩图像。 ImageWriterImageWriteParam 提供上述开箱即用的功能。

          您是否打算根据您的需求定制其他一些原因来编写自​​己的 API?

          如果不使用开箱即用的 java API,高级方法可能是: 1) 读取图像文件。 2)使用 ImageIO ,根据图像类型(jpeg,png等)获取 ImageWriter 3)从编写器获取 ImageWriteParam 对象并根据您的要求设置压缩参数。 4) 写入图像。

          如果它不适合你,请告诉我。

          【讨论】:

            【解决方案6】:

            链接中的代码描述的不是太多压缩,而是以较低密度对图像进行采样并缩小图像。所以这并不是真正的压缩,而是降低质量(像素数和大小)以使其更小。好的压缩算法通常会找到一种方法来保存图像,这种方式使用更少的空间而不会过多地影响质量(使用数学和其他东西)。

            【讨论】:

              【解决方案7】:

              裁剪图像,然后发送到服务器。它运行良好,

              在gradle中添加依赖,

              compile 'com.soundcloud.android:android-crop:1.0.1@aar'
              

              画廊意图,

              private static final int PICK_FROM_FILE = 3;
              private Uri mImageCaptureUri=null;
              File sdCard = Environment.getExternalStorageDirectory();
              String pathName;
              File filepath;
              
              Intent i = new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
              startActivityForResult(i, PICK_FROM_FILE);
              
               protected void onActivityResult(int requestCode, int resultCode, Intent data) {
                  if (resultCode != RESULT_OK)
                      return;
                   switch (requestCode) {
                           case PICK_FROM_FILE:
                             mImageCaptureUri = data.getData();
                             // METHOD FOR CROP
                             beginCrop(mImageCaptureUri);
                             System.out.println("CHECK_GALLERY_path :" + mImageCaptureUri);
                            break;
                            case Crop.REQUEST_CROP:
                            handleCrop(resultCode, data);
                            break;
                   }
                }
              
                private void beginCrop(Uri source) {
                  Uri destination = Uri.fromFile(new File(getCacheDir(), "cropped"));
                  Crop.of(source, destination).start(this);
               }
              
                private void handleCrop(int resultCode, Intent result) {
              
                  if (resultCode == RESULT_OK) {
              
                      mImageCaptureUri = Crop.getOutput(result);
              
                      pathName = mImageCaptureUri.getPath();
                      //uploadImageToServer();
                      //  getImageUri(mImageCaptureUri);
                                     Picasso.with(Activity_My_Account.this).load(mImageCaptureUri).into(getimage);          //IMAGE SAVE TO STORAGE
                      getImageUri(mImageCaptureUri);
                      System.out.println("path_name+++" + mImageCaptureUri);
              
              
                  } else if (resultCode == Crop.RESULT_ERROR) {
                      Toast.makeText(this, Crop.getError(result).getMessage(),        Toast.LENGTH_SHORT).show();
                  }
              }
              
              
                  private void getImageUri(Uri mImageCaptureUri) {
                   Bitmap photo = null;
                   try {
                      InputStream image_stream = getContentResolver().openInputStream(mImageCaptureUri);
                      java.util.Date date = new java.util.Date();
                       pathName = sdCard + "/" + "LEUF";
                          File myDir = new File(pathName);
                      System.out.println("GET_CHECK_PATH_NAME :" + pathName);
                      myDir.mkdirs();
                      Random generator = new Random();
                      int n = 10000;
                      n = generator.nextInt(n);
                      String fname = "Image-" + n + ".jpg";
                      File file = new File(myDir, fname);
                      // if (file.exists()) file.delete();
                      ByteArrayOutputStream bytes = new ByteArrayOutputStream();
                      Bitmap bitmapOrg = Bitmap.createScaledBitmap(BitmapFactory.decodeStream(image_stream), 350, 350, false);
                      photo.compress(Bitmap.CompressFormat.JPEG, 100, bytes);
                       filepath=file;
                       try {
                          // file.createNewFile();
                          FileOutputStream fo = new FileOutputStream(file);
                          fo.write(bytes.toByteArray());
                          fo.flush();
                          fo.close();
                      } catch (IOException e) {
              
                          e.printStackTrace();
                      }
                    //here call to upload image to server
                      public void UploadProfilePic(filepath){
                      }
              
                   } catch (FileNotFoundException e) {
                      e.printStackTrace();
                  }
              }
              

              【讨论】: