Help us understand the problem. What is going on with this article?

RetrofitでMediaStoreから取得したオブジェクトをアップロードする

More than 3 years have passed since last update.

Retrofit 1.9.0でファイルをアップロードするにはTypedFileを使うのが手っ取り早い。以下は最小限の定義。

public interface UploadService {
    @Multipart
    @POST("/upload")
    Observable<Response> upload(@Part("file") TypedFile file);
}

TypedFileが扱えるのはローカルファイルのみであるが、Android開発をしていてMediaStoreから取得したオブジェクトをアップロードしたいことがあったので以下のようなクラスを作成した。

public class Media implements TypedInput, TypedOutput {
    private static final int BUFFER_SIZE = 4096;

    private Context mContext;
    private Uri mUri;
    private String mDisplayName;
    private String mMimeType;
    private long mSize;

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

        ContentResolver contentResolver = mContext.getContentResolver();
        mMimeType = contentResolver.getType(uri);

        Cursor cursor = contentResolver.query(uri, null, null, null, null);
        int nameIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
        int sizeIndex = cursor.getColumnIndex(OpenableColumns.SIZE);
        cursor.moveToFirst();
        mDisplayName = cursor.getString(nameIndex);
        mSize = cursor.getLong(sizeIndex);
        cursor.close();

        // OpenableColumns.SIZE で取得したサイズは実際のバイト数より大きいことがあり
        // unexpected end of streamになる可能性があるので
        // statの値が利用できればそれを使う
        long statSize = getStatSize(contentResolver, uri);
        if (statSize > 0) {
            mSize = statSize;
        }
    }

    /**
     * Stack Overflowの記事を参考にした
     * @link http://stackoverflow.com/questions/21882322/how-to-correctly-get-the-file-size-of-an-android-net-uri
     */
    private long getStatSize(ContentResolver contentResolver, Uri uri) {
        long size = -1;
        try {
            ParcelFileDescriptor fd = contentResolver.openFileDescriptor(uri, "r");
            size = fd.getStatSize();
            try {
                fd.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        return size;
    }

    @Override
    public String fileName() {
        return mDisplayName;
    }

    @Override
    public String mimeType() {
        return mMimeType;
    }

    @Override
    public long length() {
        return mSize;
    }

    @Override
    public InputStream in() throws IOException {
        return mContext.getContentResolver().openInputStream(mUri);
    }

    @Override
    public void writeTo(OutputStream out) throws IOException {
        byte[] buffer = new byte[BUFFER_SIZE];
        InputStream in = in();
        try {
            int read;
            while ((read = in.read(buffer)) != -1) {
                out.write(buffer, 0, read);
            }
        } finally {
            in.close();
        }
    }
}

嵌まったポイントはコメントにも書いている通りOpenableColumns.SIZEが正確なサイズを返さない場合があることで、Stack Overflowの How to correctly get the file size of an android.net.Uri? に書いてある実装を使わせていただいた。

あとはメソッドの定義を追加するだけ。

public interface UploadService {
    @Multipart
    @POST("/upload")
    Observable<Response> upload(@Part("file") TypedFile file);

    @Multipart
    @POST("/upload")
    Observable<Response> upload(@Part("file") Media media);
}
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away