3

I know it's a common and recurrent question, but I didn't find any good or recent answer.

Please do not close this question. It can be a very recent reference for future Android developers who try to achieve that because most of the answers that I've seen through SO are deprecated or do not work as expected.

I want to pick one image or video from an Android device and upload it to my server.
I've already achieved pick and upload part of an image but the solution is barely good.

First, it is in Java (my whole project is in Kotlin, if I refactor directly the solution in Kotlin I get some crash on runtime).

Second, I don't think this solution is the best to achieve the same process with a video (even if the process is working on small video ~5/10 seconds, on longer video 30 to 180 seconds, I get a timeout).

How I pick a media:

private fun importPicture() {
    val intent = Intent(Intent.ACTION_GET_CONTENT)
    intent.type = "image/*"
    startActivityForResult(intent, PICK_IMAGE_REQUEST_CODE)
}

private fun importVideo(){
    val intent = Intent(Intent.ACTION_GET_CONTENT)
    intent.type = "video/*"
    startActivityForResult(intent, PICK_VIDEO_REQUEST_CODE)
}

My process to upload the media after getting the Uri: Uri -> File -> MultipartBody using Retrofit2 and RxJava2

Here is the solution that I'm using to get the File from the Uri from this answer (other answers on this post don't work !!):

import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.provider.OpenableColumns;
import android.util.Log;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

public class FileUtile {
    private static final int EOF = -1;
    private static final int DEFAULT_BUFFER_SIZE = 1024 * 4;


    public static File from(Context context, Uri uri) throws IOException {
        InputStream inputStream = context.getContentResolver().openInputStream(uri);
        String fileName = getFileName(context, uri);
        String[] splitName = splitFileName(fileName);
        File tempFile = File.createTempFile(splitName[0] + "tmp", splitName[1]);
        tempFile = rename(tempFile, fileName);
        tempFile.deleteOnExit();
        FileOutputStream out = null;
        try {
            out = new FileOutputStream(tempFile);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        if (inputStream != null) {
            copy(inputStream, out);
            inputStream.close();
        }

        if (out != null) {
            out.close();
        }
        return tempFile;
    }

    private static String[] splitFileName(String fileName) {
        String name = fileName;
        String extension = "";
        int i = fileName.lastIndexOf(".");
        if (i != -1) {
            name = fileName.substring(0, i);
            extension = fileName.substring(i);
        }

        return new String[]{name, extension};
    }

    private static String getFileName(Context context, Uri uri) {
        String result = null;
        if (uri.getScheme().equals("content")) {
            Cursor cursor = context.getContentResolver().query(uri, null, null, null, null);
            try {
                if (cursor != null && cursor.moveToFirst()) {
                    result = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (cursor != null) {
                    cursor.close();
                }
            }
        }
        if (result == null) {
            result = uri.getPath();
            int cut = result.lastIndexOf(File.separator);
            if (cut != -1) {
                result = result.substring(cut + 1);
            }
        }
        return result;
    }

    private static File rename(File file, String newName) {
        File newFile = new File(file.getParent(), newName);
        if (!newFile.equals(file)) {
            if (newFile.exists() && newFile.delete()) {
                Log.d("FileUtil", "Delete old " + newName + " file");
            }
            if (file.renameTo(newFile)) {
                Log.d("FileUtil", "Rename file to " + newName);
            }
        }
        return newFile;
    }

    private static long copy(InputStream input, OutputStream output) throws IOException {
        long count = 0;
        int n;
        byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
        while (EOF != (n = input.read(buffer))) {
            output.write(buffer, 0, n);
            count += n;
        }
        return count;
    }
}

How I build my requests:

fun uploadPost(token: String,
               file: File,
               post: Post): Completable {
    val map = HashMap<String, Any>()
    map["location"] = post.location
    map["title"] = post.title
    map["description"] = post.description
    map["categories"] = post.categories.toList()
    map["tags"] = post.tags.toList()
    map["price"] = post.priceExcT
    val body = MultipartBody.Builder()
            .setType(MultipartBody.FORM)
            .addFormDataPart("model", JSONObject(map.toMap()).toString())
            .addFormDataPart("file", file.name, RequestBody.create(MEDIA_TYPE_JPEG, file))
            .build()
    return apiService.uploadPost(token, body)
}

fun uploadVideo(token: String,
               file: File,
               post: Post): Completable {
    val map = HashMap<String, Any>()
    map["location"] = post.location
    map["title"] = post.title
    map["description"] = post.description
    map["categories"] = post.categories.toList()
    map["tags"] = post.tags.toList()
    map["price"] = post.priceExcT
    val body = MultipartBody.Builder()
            .setType(MultipartBody.FORM)
            .addFormDataPart("model", JSONObject(map.toMap()).toString())
            .addFormDataPart("file", file.name, RequestBody.create(MEDIA_TYPE_MP4, file))
            .build()
    return apiService.uploadVideo(token, body)//simple POST in ApiService which is an Interface
}

I already tried several and simple solutions as uri.toFile() of course it doesn't work.

If you need any more information or snippet please ask me.

Itoun
  • 3,713
  • 5
  • 27
  • 47
  • Per the stack trace, the problem is your logging interceptor, you are attempting to print an enormously large byte array as a string. Disable your interceptor and it'll work, then figure out how to print only what's relevant. – Francesc May 20 '20 at 15:37
  • I'm doing it directly and update the question accordingly! However, the question is still valid to find a way to get a file from the Uri obtained after the media selected ! – Itoun May 20 '20 at 15:39
  • Nobody is saying your question is not valid. I'm saying that your stack trace tells you what the problem is and how to fix it. – Francesc May 20 '20 at 15:41
  • Indeed. I edited my question to remove the crash part because as you said it came from the `logging interceptor`. – Itoun May 20 '20 at 15:51
  • 1
    Create an `InputStreamRequestBody` based on [this issue comment](https://github.com/square/okhttp/issues/3585#issuecomment-327319196) and use that to work directly with your `Uri`, instead of fussing with creating a file. – CommonsWare May 20 '20 at 15:58
  • @CommonsWare thanks for the suggestion. But we already discussed about that with my teammate who is developing the API and he needs to get a file in the POST Request. So I can't use this solution. – Itoun May 20 '20 at 17:07
  • 1
    "So I can't use this solution" -- sure you can. The server neither knows nor cares whether the data in the `POST` request comes from a file, a piece of content, a `BLOB` column in a database, or is generated on the fly. The `POST` request itself would look the same in all of those cases. – CommonsWare May 20 '20 at 17:23
  • No because I need to use a `MultipartBody` which include a file with .jpeg/.png (for image) and .mp4 for video. – Itoun May 20 '20 at 17:41
  • @CommonsWare or maybe do you mean that on this line `.addFormDataPart("file", file.name, RequestBody.create(MEDIA_TYPE_MP4, file))` I should use the RequestBody in the feature request that you said above? – Itoun May 20 '20 at 17:48
  • @Itoun: Correct. `RequestBody.create()` creates a `RequestBody` based on a file. Swap that with a custom `RequestBody` that works with the `Uri` and a `ContentResolver`, allowing you to skip the intermediate file creation step. – CommonsWare May 20 '20 at 18:04
  • I will try that! Thanks! I will let you know if it works. – Itoun May 20 '20 at 19:31

0 Answers0