从相册选择图片上传,框架使用的是Rx + Retrofit + OKHttp。因为此版本使用了MVVM架构,targetSdkVersion升到了29。在上传图片出现了报错 java.io.FileNotFoundException: /storage/emulated/0/DCIM/Camera/IMG20200608195140.jpg: open failed: EACCES (Permission denied) 原因是在android10开始,Google修改了文件相关权限,对于写入和读取文件,都有了新的一套机制,介绍内容网上很多,各大厂商也有介绍,在此不做赘述。 此解决方法适用于能获取到文件的Uri,比如打开相册选择图片,那么它的onActivityResult中的Intent data就有选择的图片的Uri。 打开图库的方法不变: 和onActivityResult回调: 先附上Android10之前我的图片上传 大同小异,主要就是获取文件File类型实例,然后构建RequestBody,使用Retrofit上传 Android Q不行了,会报错,那么主要就是使用到file的地方要修改,先看看RequestBody.create还能带什么参数: 从上文可以看出除了一个file。其他大部分都是使用RequestBody create(final MediaType contentType, final byte[] content, final int offset, final int byteCount) 那么我们也尝试使用byte[]作为上传参数,现在就需要把文件转为byte[]。 直接上代码,从onActivityResult开始: 因为我分了好几个文件,包括这些代码也只是暂时写在onActivityResult里,你们只需要看,获取到关键数据后怎么做,然后自己组装一下,应该容易的。在获取到byte[]后,使用byte[]构建RequestBody: { 底下那个“androidImg”是错的,这个是设置文件名称,假如说你填写了这个的话,那么就无法识别文件类型,后台存储的是一个无类型的文件,你再下载的话无法打开,只用图片上传以及使用Glide框架显示的,没这个问题,给Glide点个赞! } 重新把构建List<MultipartBody.Part>方法改一下,增加一个文件名,这个文件名称只需要最后有个后缀就行了,前面带文件路径也没关系,这是PostMan调用文件上传接口时的数据: 那么文件名获取,也是使用Uri获取文件名: 那么再来构建List<MultipartBody.Part>: 把上面获取到的文件路径传入这个方法中,之后使用Retrofit上没有差别因为接口传入参数类型没变 附上失败和成功的log: 使用file传文件 使用byte[]传文件: 数据返回正常 之后应该会再写一个使用相册拍照的功能。
/** * 选择照片 */ public static void pickPhoto(Activity activity) { Intent intent; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { intent = new Intent( Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI); } else { intent = new Intent( Intent.ACTION_GET_CONTENT); intent.setType("image/*"); } activity.startActivityForResult(intent, 2001); }
@Override protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { super.onActivityResult(requestCode, resultCode, data); if (data == null) { return; } if (resultCode == Activity.RESULT_OK) { switch (requestCode) { case 2001: Uri originalUri = data.getData(); String path = UriUtil.getPath(this, originalUri); if (path != null && new File(path).exists()) { File mFile = new File(path); viewModel.updateImg(mFile); } break; } } }
//从上文中获取了文件 public static List<MultipartBody.Part> getFileBody(File file) { MultipartBody.Builder builder = new MultipartBody.Builder() .setType(MultipartBody.FORM);//表单类型 RequestBody body = RequestBody.create(MediaType.parse("multipart/form-data"), file);//表单类型 builder.addFormDataPart("file", file.getName(), body); return builder.build().parts(); } //Retrofit Api 接口 /** * 文件上传 */ @Multipart @POST("xxxx") Observable<ObjResponse<ShowImageTypeData>> updateFile(@Part List<MultipartBody.Part> partLis); //调用接口 public Observable<ShowImageTypeData> updateImg(File file) { return api.updateFile(getFileBody(file)) .compose(new ObjTransform<>(null)); }
public abstract class RequestBody { /** Returns the Content-Type header for this body. */ public abstract MediaType contentType(); /** * Returns the number of bytes that will be written to {@code out} in a call to {@link #writeTo}, * or -1 if that count is unknown. */ public long contentLength() throws IOException { return -1; } /** Writes the content of this request to {@code out}. */ public abstract void writeTo(BufferedSink sink) throws IOException; /** * Returns a new request body that transmits {@code content}. If {@code contentType} is non-null * and lacks a charset, this will use UTF-8. */ public static RequestBody create(MediaType contentType, String content) { Charset charset = Util.UTF_8; if (contentType != null) { charset = contentType.charset(); if (charset == null) { charset = Util.UTF_8; contentType = MediaType.parse(contentType + "; charset=utf-8"); } } byte[] bytes = content.getBytes(charset); return create(contentType, bytes); } /** Returns a new request body that transmits {@code content}. */ public static RequestBody create(final MediaType contentType, final ByteString content) { return new RequestBody() { @Override public MediaType contentType() { return contentType; } @Override public long contentLength() throws IOException { return content.size(); } @Override public void writeTo(BufferedSink sink) throws IOException { sink.write(content); } }; } /** Returns a new request body that transmits {@code content}. */ public static RequestBody create(final MediaType contentType, final byte[] content) { return create(contentType, content, 0, content.length); } /** Returns a new request body that transmits {@code content}. */ public static RequestBody create(final MediaType contentType, final byte[] content, final int offset, final int byteCount) { if (content == null) throw new NullPointerException("content == null"); Util.checkOffsetAndCount(content.length, offset, byteCount); return new RequestBody() { @Override public MediaType contentType() { return contentType; } @Override public long contentLength() { return byteCount; } @Override public void writeTo(BufferedSink sink) throws IOException { sink.write(content, offset, byteCount); } }; } /** Returns a new request body that transmits the content of {@code file}. */ public static RequestBody create(final MediaType contentType, final File file) { if (file == null) throw new NullPointerException("content == null"); return new RequestBody() { @Override public MediaType contentType() { return contentType; } @Override public long contentLength() { return file.length(); } @Override public void writeTo(BufferedSink sink) throws IOException { Source source = null; try { source = Okio.source(file); sink.writeAll(source); } finally { Util.closeQuietly(source); } } }; } }
@Override protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { super.onActivityResult(requestCode, resultCode, data); if (data == null) { return; } if (resultCode == Activity.RESULT_OK) { switch (requestCode) { case 2001: Uri originalUri = data.getData(); try { ParcelFileDescriptor parcelFileDescriptor = getContentResolver().openFileDescriptor(originalUri, "r"); FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor(); FileInputStream fis = new FileInputStream(fileDescriptor); ByteArrayOutputStream swapStream = new ByteArrayOutputStream(); byte[] buff = new byte[1024*4]; //buff用于存放循环读取的临时数据 int rc = 0; while ((rc = fis.read(buff, 0, 100)) > 0) { swapStream.write(buff, 0, rc); } byte[] in_b = swapStream.toByteArray(); //in_b为转换之后的结果 viewModel.updateImg(in_b); } catch (IOException e) { e.printStackTrace(); } break; } } }
public static List<MultipartBody.Part> getFileBody(byte[] file) { MultipartBody.Builder builder = new MultipartBody.Builder() .setType(MultipartBody.FORM);//表单类型 RequestBody body = RequestBody.create(MediaType.parse("multipart/form-data"), file);//表单类型 builder.addFormDataPart("file", "androidImg", body); return builder.build().parts(); }
POST /api/file-application/upload HTTP/1.1 Host: 192.168.16.36:8079 Authorization: Bearer xxxx cache-control: no-cache Postman-Token: 2ff47803-aadb-4aeb-90d4-cfe342cbf78e Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW Content-Disposition: form-data; name="file"; filename="C:UsersxxDesktopQQ截图20190301164159.png ------WebKitFormBoundary7MA4YWxkTrZu0gW-- 说明一下,这个filename="xxx" 这个xxx就是刚刚填写的androidImg
/** * 获取对应uri的path * * @param context * @param uri * @return */ @SuppressLint("NewApi") public static String getPath(final Context context, final Uri uri) { final boolean isKitKat = Build.VERSION.SDK_INT >= 19; // DocumentProvider if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) { // ExternalStorageProvider if (isExternalStorageDocument(uri)) { final String docId = DocumentsContract.getDocumentId(uri); final String[] split = docId.split(":"); final String type = split[0]; if ("primary".equalsIgnoreCase(type)) { return Environment.getExternalStorageDirectory() + "/" + split[1]; } // handle non-primary volumes } // DownloadsProvider else if (isDownloadsDocument(uri)) { final String id = DocumentsContract.getDocumentId(uri); final Uri contentUri = ContentUris.withAppendedId( Uri.parse("content://downloads/public_downloads"), Long.parseLong(id)); return getDataColumn(context, contentUri, null, null); } // MediaProvider else if (isMediaDocument(uri)) { final String docId = DocumentsContract.getDocumentId(uri); final String[] split = docId.split(":"); final String type = split[0]; Uri contentUri = null; if ("image".equals(type)) { contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI; } else if ("video".equals(type)) { contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI; } else if ("audio".equals(type)) { contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI; } final String selection = "_id=?"; final String[] selectionArgs = new String[]{ split[1] }; return getDataColumn(context, contentUri, selection, selectionArgs); } } // MediaStore (and general) else if ("content".equalsIgnoreCase(uri.getScheme())) { // Return the remote address if (isGooglePhotosUri(uri)) return uri.getLastPathSegment(); return getDataColumn(context, uri, null, null); } // File else if ("file".equalsIgnoreCase(uri.getScheme())) { return uri.getPath(); } return null; }
public static List<MultipartBody.Part> getFileBody(byte[] file, String fileName) { MultipartBody.Builder builder = new MultipartBody.Builder() .setType(MultipartBody.FORM);//表单类型 RequestBody body = RequestBody.create(MediaType.parse("multipart/form-data"), file);//表单类型 builder.addFormDataPart("file", fileName, body); return builder.build().parts(); }
api.updateFile(AppUtils.getFileBody(file, fileName)) .compose(new ObjTransform<>(null));
--> POST https://192.168.16.36:8079/xxxxx http/1.1 Content-Type: multipart/form-data; boundary=36717d28-648b-44b2-a904-07b23fa5fdb8 Content-Length: 115873 Cache-Control: no-cache Authorization: Bearer eyJhbGciOiJIUzUxMiJ9xxxxxx Host: 192.168.16.36:8079 Connection: Keep-Alive Accept-Encoding: gzip User-Agent: okhttp/3.6.0 --> END POST <-- HTTP FAILED: java.io.FileNotFoundException: /storage/emulated/0/tieba/FCF10754FF8F4014D623FF8F8B937C1D.jpg: open failed: EACCES (Permission denied)
--> POST https://192.168.16.36:8079/xxxxx http/1.1 Content-Type: multipart/form-data; boundary=36717d28-648b-44b2-a904-07b23fa5fdb8 Content-Length: 115873 Cache-Control: no-cache Authorization: Bearer eyJhbGciOiJIUzUxMiJ9xxxxxx Host: 192.168.16.36:8079 Connection: Keep-Alive Accept-Encoding: gzip User-Agent: okhttp/3.6.0 --> END POST <-- 200 https://192.168.16.36:8079/xxxxx (213ms) Server: nginx/1.11.6 Date: Wed, 10 Jun 2020 03:38:10 GMT Content-Type: application/json Transfer-Encoding: chunked Connection: keep-alive <-- END HTTP {"code":1,"subCode":null,"message":"成功","data":xxxxx}
本网页所有视频内容由 imoviebox边看边下-网页视频下载, iurlBox网页地址收藏管理器 下载并得到。
ImovieBox网页视频下载器 下载地址: ImovieBox网页视频下载器-最新版本下载
本文章由: imapbox邮箱云存储,邮箱网盘,ImageBox 图片批量下载器,网页图片批量下载专家,网页图片批量下载器,获取到文章图片,imoviebox网页视频批量下载器,下载视频内容,为您提供.
阅读和此文章类似的: 全球云计算