简体   繁体   English

Android-从磁盘上传图像

[英]Android - Upload an Image From Disk

I am running into some issues upload a file on Android. 我在Android上上传文件时遇到了一些问题。 I've kind of cobbled together this section of the app, and it now needs some reworking. 我已经将应用程序的这一部分拼凑在一起,现在需要进行一些修改。

I'm attempting to upload, from an on disk image, referenced by a Uri, a file to a server. 我正在尝试从Uri引用的磁盘映像上将文件上传到服务器。

Prior to uploading, I'm attempting to scale the image down, respecting aspect ratio, to a max dimension of 1280. 在上传之前,我尝试按照宽高比将图像缩小到最大尺寸1280。

Here is a sample class with the actual code that I'm using. 这是一个示例类,其中包含我正在使用的实际代码。 I'm sure it's horribly inefficient: 我敢肯定这是非常低效的:

/**
 * This is a fake class, this is actually spread across 2 or 3 files
 */
public class Temp
{
  /**
   * This is used to return an Input stream of known size
   */
  public static class KnownSizeInputStream extends InputStreamBody
  {
    private int mLength;

    public KnownSizeInputStream( final InputStream in, final int length, final String mimeType, final String filename )
    {
      super( in, mimeType, filename );
      mLength = length;
    }

    public long getContentLength()
    {
      return mLength;
    }
  }

  private static final int MAX_WIDTH  = 1280;
  private static final int MAX_HEIGHT = 1280;

  /**
   * Open up a file on disk and convert it into a stream of known size
   */
  public KnownSizeInputStream toStreamAio( Context c, Uri path )
  {
    /**
     * Scale down bitmap
     */
    Bitmap bitmapData = null;

    try
    {
      bitmapData = BitmapFactory.decodeStream( c.getContentResolver().openInputStream( path ) );
    }
    catch( Exception e )
    {
      e.printStackTrace();
    }

    int imgWidth = bitmapData.getWidth();
    int imgHeight = bitmapData.getHeight();

    // Constrain to given size but keep aspect ratio
    float scaleFactor = Math.min( ( ( float )MAX_WIDTH ) / imgWidth, ( ( float )MAX_HEIGHT ) / imgHeight );

    Matrix scale = new Matrix();
    scale.postScale( scaleFactor, scaleFactor );
    final Bitmap scaledImage = Bitmap.createBitmap( bitmapData, 0, 0, imgWidth, imgHeight, scale, false );

    try
    {
      bitmapData = scaledImage.copy( scaledImage.getConfig(), true );

      scaledImage.recycle();
    }
    catch( Exception e )
    {
      e.printStackTrace();
    }

    /**
     * To byte[]
     */
    byte[] byteData = null;

    ByteArrayOutputStream baos = new ByteArrayOutputStream();

    bitmapData.compress( Bitmap.CompressFormat.JPEG, 100, baos );

    byteData = baos.toByteArray();

    /**
     * To stream
     */
    return new KnownSizeInputStream( new ByteArrayInputStream( byteData ), byteData.length, "image/jpg", "Some image" );
  }

  /** 
   * Some pieces are removed, the main part is the addPart line
   */
  public void doUpload()
  {
    // create a new HttpPost, to our specified URI
    HttpPost post = new HttpPost( postUri );

    // org.apache.http.entity.mime
    MultipartEntity entity = new MultipartEntity( HttpMultipartMode.STRICT );


    // This line starts all of the issues
    entity.addPart( "file", toStreamAio( mContext, Uri.parse( "/some/file.jpg" ) ) );


    post.setEntity( entity );

    // send it
    HttpResponse response = client.execute( post );

  }
}

Here is the exception I'm getting, I'm guessing from the resize attempting to allocate the full size of the image: 这是我得到的异常,我从调整大小的猜测中尝试分配图像的完整大小:

Caused by: java.lang.OutOfMemoryError
 at android.graphics.Bitmap.nativeCopy(Native Method)
 at android.graphics.Bitmap.copy(Bitmap.java:403)
 at com.app.helper.UploadableImage.toScaledBitmap(UploadableImage.java:170)
 at com.app.helper.UploadableImage.toByteArray(UploadableImage.java:53)
 at com.app.helper.UploadableImage.toStream(UploadableImage.java:242)
 at com.app.rest.task.UploadContentTask.doInBackground(UploadContentTask.java:80)
 at com.app.rest.task.UploadContentTask.doInBackground(UploadContentTask.java:1)
 at android.os.AsyncTask$2.call(AsyncTask.java:264)
 at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:305)
 ... 5 more

It's being triggered by this line: 这是由以下行触发的:

data = scaledImage.copy( scaledImage.getConfig(), true );

I guess the main thing I'm asking is, how do I get an image from a path on Disk, to a scaled image, to a stream I can put into: 我想我要问的主要问题是,如何从磁盘上的路径获取图像,缩放图像以及可以放入的流:

org.apache.http.entity.mime.MultipartEntity

via: 通过:

.addPart("file", streamData);

Most efficiently, assuming the images can be massive (~6000px is the biggest dimension I've hit so far) 最有效的是,假设图像可能很大(到目前为止,我达到的最大尺寸是〜6000px)

Firstly, why do you have to make a copy of the scaled bitmap? 首先,为什么要复制缩放的位图? Can't you compress the scaled bitmap directly like this: 您不能像这样直接压缩缩放的位图:

final Bitmap scaledImage = Bitmap.createBitmap(bitmapData, 0, 0,
        imgWidth, imgHeight, scale, false);
scaledImage.compress(Bitmap.CompressFormat.JPEG, 100, baos);

If you can avoid the copy, you could avoid getting the OutOfMemoryError . 如果可以避免复制,则可以避免获取OutOfMemoryError

Also you can achieve good compression with imperceptible loss in quality even if you choose a quality of 95% using JPEG compression (when working with photographs of natural objects). 此外,即使使用JPEG压缩(在处理自然物体的照片时)选择95%的质量,也可以实现良好的压缩而不会造成质量损失。 You should experiment with the quality setting and check for yourself. 您应该尝试质量设置并亲自检查一下。

Here is the complete class that is now working for me. 这是现在正在为我工​​作的完整课程。 You load it with a Context and a Uri, and call any of the three public methods: 您使用Context和Uri加载它,并调用以下三个公共方法中的任何一个:

public class UploadableImage
{
  private static final int MAX_WIDTH  = 1280;
  private static final int MAX_HEIGHT = 1280;

  private Uri              mUri;

  private String           mImageName;

  private Context          mContext;

  public UploadableImage( Context context )
  {
    mContext = context;

    generateFilename();
  }

  public UploadableImage( Context context, Uri uri )
  {
    mContext = context;
    mUri = uri;

    generateFilename();
  }

  // TODO Generate...
  private void generateFilename()
  {
    mImageName = UUID.randomUUID().toString() + ".jpg";
  }

  public void setUri( Uri uri )
  {
    mUri = uri;
  }

  public Bitmap toBitmap()
  {
    try
    {
      InputStream input = mContext.getContentResolver().openInputStream( mUri );

      BitmapFactory.Options readOptions = new BitmapFactory.Options();
      readOptions.inJustDecodeBounds = true;

      BitmapFactory.decodeStream( input, null, readOptions );

      input.close();

      // Raw height and width of image
      final int height = readOptions.outHeight;
      final int width = readOptions.outWidth;

      int inSampleSize = 1;

      if( height > MAX_HEIGHT || width > MAX_WIDTH )
      {
        if( width > height )
        {
          float result = ( float )height / ( float )MAX_HEIGHT;

          inSampleSize = ( int )FloatMath.ceil( result );
        }
        else
        {
          float result = ( float )width / ( float )MAX_WIDTH;

          inSampleSize = ( int )FloatMath.ceil( result );
        }
      }

      return toBitmap( inSampleSize );
    }
    catch( Exception e )
    {
      e.printStackTrace();
    }

    return null;
  }

  public Bitmap toBitmap( int sampleSize )
  {
    try
    {
      InputStream input = mContext.getContentResolver().openInputStream( mUri );

      input = mContext.getContentResolver().openInputStream( mUri );

      // Decode bitmap with inSampleSize set
      BitmapFactory.Options scaleOptions = new BitmapFactory.Options();

      scaleOptions.inJustDecodeBounds = false;
      scaleOptions.inSampleSize = sampleSize;

      Bitmap scaledBitmap = BitmapFactory.decodeStream( input, null, scaleOptions );

      input.close();

      return scaledBitmap;
    }
    catch( Exception e )
    {
      e.printStackTrace();
    }

    return null;
  }

  public KnownSizeInputStream toMimeStream()
  {
    Bitmap scaledBitmap = toBitmap();

    ByteArrayOutputStream stream = new ByteArrayOutputStream();

    scaledBitmap.compress( Bitmap.CompressFormat.JPEG, 95, stream );

    byte[] byteArray = stream.toByteArray();

    return new KnownSizeInputStream( new ByteArrayInputStream( byteArray ), byteArray.length, "image/jpg", mImageName );
  }

  public String toString()
  {
    return "UploadableImage, Uri: " + mUri;
  }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM