其实, 我是个小白... 如果我写的东西 有错误,麻烦指出来。。。

多文件上传 与 进度回调

前端做Http请求偶尔会遇到 multipartform-data 表单提交的问题,我们先看 OKHttp拦截器打印(印度的印)的参数, 我一共提交了4个文件, 3个字符串

LogTrack  warn  org.alex.okhttp [ (HttpLogInterceptor.java:145)#printLog]  打印请求参数:
POST  contentType = multipartform-data
请求体:
Content-Disposition: form-data; name="file"; filename="黄喉蜂虎.png"
Content-Type: image/png
Content-Length: 321229
富媒体文件, 无法用字符串描述
7533c20a-9a0b-4c3d-8e3d-c0af3fae4d32
Content-Disposition: form-data; name="userPortrait"; filename="黄喉蜂虎.png"
Content-Type: image/png
Content-Length: 321229
富媒体文件, 无法用字符串描述
7533c20a-9a0b-4c3d-8e3d-c0af3fae4d32
Content-Disposition: form-data; name="file"; filename="001.gif"
Content-Type: image/gif
Content-Length: 123453
富媒体文件, 无法用字符串描述
7533c20a-9a0b-4c3d-8e3d-c0af3fae4d32
Content-Disposition: form-data; name="file"; filename="b5h4.mp4"
Content-Type: application/octet-stream
Content-Length: 7159248
富媒体文件, 无法用字符串描述
7533c20a-9a0b-4c3d-8e3d-c0af3fae4d32
Content-Disposition: form-data; name="id"
Content-Length: 1
1
7533c20a-9a0b-4c3d-8e3d-c0af3fae4d32
Content-Disposition: form-data; name="phone"
Content-Length: 11
131460xxxxx
7533c20a-9a0b-4c3d-8e3d-c0af3fae4d32
Content-Disposition: form-data; name="pwd"
Content-Length: 6
123456
7533c20a-9a0b-4c3d-8e3d-c0af3fae4d32LogTrack  error  com.alex.httpapp.module.fileupload [ (FileUploadView.kt:31)#FileUploadView$onCreateData$1#onNext]  WrapperBean{code='100', message='SUCCESS', data=上传成功}
LogTrack  error  com.alex.httpapp.baselibrary.mvp [ (AbsView.kt:30)#onCreate]  **************     1     继续请求,其他取消请求      ************** 
LogTrack  warn  org.alex.okhttp [ (HttpLogInterceptor.java:145)#printLog]  打印返回数据:
POST http://127.0.0.1:8081/AppServer/uploadUserPortrait (709ms)
body:{"code":"100","message":"SUCCESS","requestUrl":"http://127.0.0.1:8081/AppServer/uploadUserPortrait","data":"上传成功"}

在看看进度


LogTrack  warn  com.alex.httpapp.module.fileupload [ (FileUploadModel.kt:37)#FileUploadModel$uploadUserPortrait$onUploadListener$1#onUploadProgress]  7926217  8192  0.0
LogTrack  warn  com.alex.httpapp.module.fileupload [ (FileUploadModel.kt:37)#FileUploadModel$uploadUserPortrait$onUploadListener$1#onUploadProgress]  7926217  16384  0.0
LogTrack  warn  com.alex.httpapp.module.fileupload [ (FileUploadModel.kt:37)#FileUploadModel$uploadUserPortrait$onUploadListener$1#onUploadProgress]  7926217  24576  0.0
LogTrack  warn  com.alex.httpapp.module.fileupload [ (FileUploadModel.kt:37)#FileUploadModel$uploadUserPortrait$onUploadListener$1#onUploadProgress]  7926217  32768  0.0
LogTrack  warn  com.alex.httpapp.module.fileupload [ (FileUploadModel.kt:37)#FileUploadModel$uploadUserPortrait$onUploadListener$1#onUploadProgress]  7926217  40960  0.0
LogTrack  warn  com.alex.httpapp.module.fileupload [ (FileUploadModel.kt:37)#FileUploadModel$uploadUserPortrait$onUploadListener$1#onUploadProgress]  7926217  49152  0.0
LogTrack  warn  com.alex.httpapp.module.fileupload [ (FileUploadModel.kt:37)#FileUploadModel$uploadUserPortrait$onUploadListener$1#onUploadProgress]  7926217  57344  0.0
LogTrack  warn  com.alex.httpapp.module.fileupload [ (FileUploadModel.kt:37)#FileUploadModel$uploadUserPortrait$onUploadListener$1#onUploadProgress]  7926217  65536  0.0
LogTrack  warn  com.alex.httpapp.module.fileupload [ (FileUploadModel.kt:37)#FileUploadModel$uploadUserPortrait$onUploadListener$1#onUploadProgress]  7926217  73728  0.0
LogTrack  warn  com.alex.httpapp.module.fileupload [ (FileUploadModel.kt:37)#FileUploadModel$uploadUserPortrait$onUploadListener$1#onUploadProgress]  7926217  81920  1.0
LogTrack  warn  com.alex.httpapp.module.fileupload [ (FileUploadModel.kt:37)#FileUploadModel$uploadUserPortrait$onUploadListener$1#onUploadProgress]  7926217  90112  1.0
LogTrack  warn  com.alex.httpapp.module.fileupload [ (FileUploadModel.kt:37)#FileUploadModel$uploadUserPortrait$onUploadListener$1#onUploadProgress]  7926217  98304  1.0
LogTrack  warn  com.alex.httpapp.module.fileupload [ (FileUploadModel.kt:37)#FileUploadModel$uploadUserPortrait$onUploadListener$1#onUploadProgress]  7926217  106496  1.0
......LogTrack  warn  com.alex.httpapp.module.fileupload [ (FileUploadModel.kt:37)#FileUploadModel$uploadUserPortrait$onUploadListener$1#onUploadProgress]  7926217  7905280  99.0
LogTrack  warn  com.alex.httpapp.module.fileupload [ (FileUploadModel.kt:37)#FileUploadModel$uploadUserPortrait$onUploadListener$1#onUploadProgress]  7926217  7913472  99.0
LogTrack  warn  com.alex.httpapp.module.fileupload [ (FileUploadModel.kt:37)#FileUploadModel$uploadUserPortrait$onUploadListener$1#onUploadProgress]  7926217  7921664  99.0
LogTrack  warn  com.alex.httpapp.module.fileupload [ (FileUploadModel.kt:37)#FileUploadModel$uploadUserPortrait$onUploadListener$1#onUploadProgress]  7926217  7926217  100.0

看一下 后端的 接受情况

LogTrack[ (Native Method) #invoke0] 匹配到 2017-07-23 22:17:18  UploadController  uploadUserPortrait    (com.alex.appserver.module.upload.UploadController)请求参数:拼接串:http://127.0.0.1:8081/AppServer/uploadUserPortrait请求行:id=1&phone=13146008029&pwd=123456
LogTrack[ (Native Method) #invoke0] ds2  UserEntity com.alex.appserver.module.usercenter.UserService.findEntityById(String)
2017-07-23 22:17:18.169 debug 12772 --- [.1-8081-exec-10] c.a.a.m.u.UserDao.findEntityById         : ==>  Preparing: select nickname, gender, phone, account_balance, point_balance from t_user where id=?
2017-07-23 22:17:18.169 debug 12772 --- [.1-8081-exec-10] c.a.a.m.u.UserDao.findEntityById         : ==> Parameters: 1(String)
2017-07-23 22:17:18.171 debug 12772 --- [.1-8081-exec-10] c.a.a.m.u.UserDao.findEntityById         : <==      Total: 1
LogTrack[ (Native Method) #invoke0] ds2  UserEntity com.alex.appserver.module.usercenter.UserService.findEntityById(String)
LogTrack[ (<generated>) UploadController$$FastClassBySpringCGLIB$$7a2c9a15#invoke] UserEntity{id=null, nickname='张三2', gender='1', phone='13146008029', pwd='null', accountBalance=null, pointBalance=null}
LogTrack[ (<generated>) UploadController$$FastClassBySpringCGLIB$$7a2c9a15#invoke] D:/WorkSpace/DataStore/AppServer/FileUpload/file/黄喉蜂虎.png 耗时  1 毫秒
LogTrack[ (<generated>) UploadController$$FastClassBySpringCGLIB$$7a2c9a15#invoke] D:/WorkSpace/DataStore/AppServer/FileUpload/file/001.gif 耗时  0 毫秒
LogTrack[ (<generated>) UploadController$$FastClassBySpringCGLIB$$7a2c9a15#invoke] D:/WorkSpace/DataStore/AppServer/FileUpload/file/b5h4.mp4 耗时  35 毫秒
LogTrack[ (<generated>) UploadController$$FastClassBySpringCGLIB$$7a2c9a15#invoke] D:/WorkSpace/DataStore/AppServer/FileUpload/userPortrait/黄喉蜂虎.png 耗时  4 毫秒
LogTrack[ (<generated>) UploadController$$FastClassBySpringCGLIB$$7a2c9a15#invoke] 所有文件耗时  40 毫秒
LogTrack[ (ResponseUtil.java:35) #resp] 返回参数:{"code":"100","message":"SUCCESS","data":"上传成功"}

本地磁盘有没有呢? 真的是有的,你不应该怀疑我的能力

Paste_Image.png
Paste_Image.png

先看Android 端的代码

原谅我用 ItelliJ 写的烂代码


interface ApiService {@POST("uploadUserPortrait")fun uploadUserPortrait(@Body body: RequestBody): Observable<WrapperBean<Any>>
}class FileUploadModel : AbsModel(), FileUploadContract.Model {override fun uploadUserPortrait(): Observable<WrapperBean<Any>> {val mp4File = File(AppCon.CacheEnum.TMP_MP4_PATH)val gifFile = File(AppCon.CacheEnum.TMP_GIF_PATH)val pngFile = File(AppCon.CacheEnum.TMP_PNG_PATH)val builder = MultipartBody.Builder().setType(MultipartBody.FORM)builder.addFormDataPart("file", pngFile.name, pngFile.guessRequestBody())builder.addFormDataPart("userPortrait", pngFile.name, pngFile.guessRequestBody())builder.addFormDataPart("file", gifFile.name, gifFile.guessRequestBody())builder.addFormDataPart("file", mp4File.name, mp4File.guessRequestBody())builder.addFormDataPart("id", "1")builder.addFormDataPart("phone", "13146008029")builder.addFormDataPart("pwd", "123456")val onUploadListener = OnUploadListener { countLength, currLength, progress ->LogTrack.w("$countLength  $currLength  $progress")}return RetrofitUtil.getService(onUploadListener).uploadUserPortrait(builder.build())}}fun String.guessMimeType(): String {val fileNameMap = URLConnection.getFileNameMap()var contentTypeFor: String? = nulltry {contentTypeFor = fileNameMap.getContentTypeFor(URLEncoder.encode(this, "UTF-8"))} catch (e: UnsupportedEncodingException) {e.printStackTrace()}if (contentTypeFor == null) {contentTypeFor = "application/octet-stream"}return contentTypeFor
}fun File.guessRequestBody(): RequestBody = RequestBody.create(MediaType.parse(this.name.guessMimeType()), this)
UploadProgressInterceptor
public class UploadProgressInterceptor implements Interceptor {OnUploadListener onUploadListener;public UploadProgressInterceptor setOnUploadListener(OnUploadListener onUploadListener) {this.onUploadListener = onUploadListener;return this;}@Overridepublic Response intercept(Chain chain) throws IOException {Request originalRequest = chain.request();if (originalRequest.body() == null || onUploadListener == null) {return chain.proceed(originalRequest);}ProgressRequestBody progressRequestBody = new ProgressRequestBody().setOriginalRequestBody(originalRequest.body()).setOnUploadListener(onUploadListener);Request progressRequest = originalRequest.newBuilder().method(originalRequest.method(), progressRequestBody).build();return chain.proceed(progressRequest);}
}class ProgressRequestBody extends RequestBody {private RequestBody originalRequestBody;private OnUploadListener onUploadListener;private CountingSink countingSink;public ProgressRequestBody setOnUploadListener(OnUploadListener onUploadListener) {this.onUploadListener = onUploadListener;return this;}public ProgressRequestBody setOriginalRequestBody(RequestBody requestBody) {this.originalRequestBody = requestBody;return this;}@Overridepublic MediaType contentType() {return originalRequestBody.contentType();}@Overridepublic long contentLength() {try {return originalRequestBody.contentLength();} catch (IOException e) {e.printStackTrace();}return -1;}@Overridepublic void writeTo(BufferedSink sink) throws IOException {BufferedSink bufferedSink;countingSink = new CountingSink(sink);bufferedSink = Okio.buffer(countingSink);originalRequestBody.writeTo(bufferedSink);bufferedSink.flush();}protected final class CountingSink extends ForwardingSink {private long bytesWritten = 0;public CountingSink(Sink delegate) {super(delegate);}@Overridepublic void write(Buffer source, long byteCount) throws IOException {super.write(source, byteCount);bytesWritten += byteCount;//listener.progress("正在上传", bytesWritten, contentLength());double progress = (bytesWritten * 100 / contentLength());//LogTrack.w("progress = " + progress);if (onUploadListener != null) {onUploadListener.onUploadProgress(contentLength(), bytesWritten, progress);}}}
}public interface OnUploadListener {/*** 进度监听** @param countLength 总 字节数* @param currLength  当前 已经上传的 字节数* @param progress    上传的 进度值  99.99   [0.00, 100.00]  展示2位小数 的double*/void onUploadProgress(long countLength, long currLength, double progress);}

为什么 有 java 也有 kotlin

怪我咯,,, 我比较懒惰的

再说说 SpringBoot 遇到的无知

  • 关于 druid 我按照网上的配置,

感觉没有 任何 问题啊, 就是看不到任何 效果? 开始怀疑人生了, 我有这么笨吗?
事实上 我已经做出来了, 只是我对力量一无所知, 只要在浏览器访问以下就可以了,

  • 怪我咯。。。

http://127.0.0.1:8081/druid/weburi.html

Paste_Image.png
Paste_Image.png
  • 再说另外一个 上传文件限制的问题

整体门阀是100MB, 具体接口暂时没做限制, 以后再写吧

spring.http.multipart.max-file-size=100MB
spring.http.multipart.max-request-size=100MB@Configuration
public class MultipartConfiguration {@Beanpublic MultipartConfigElement multipartConfigElement() {MultipartConfigFactory config = new MultipartConfigFactory();config.setMaxFileSize("100MB");config.setMaxRequestSize("100MB");return config.createMultipartConfig();}
}

UploadController

这里 要用 MultipartHttpServletRequest 不能用 HttpServletRequest , 否则会报

org.apache.catalina.connector.RequestFacade cannot be cast to org.springframework.web.multipart.MultipartHttpServletRequest
Paste_Image.png

@Controller
@RequestMapping(value = AppCon.BASE_URL)
public class UploadController extends AbsController<UserService> {/*** 上传 用户 头像** @return*/@FormWholeCheck@ResponseBody@RequestMapping(value = {"uploadUserPortrait", "UploadUserPortrait"}, method = {RequestMethod.POST/*, RequestMethod.GET*/})public Wrapper uploadUserPortrait(String id, MultipartHttpServletRequest request) throws IOException {UserEntity userEntity = getService().findEntityById(id);LogTrack.w(userEntity);if (TrivialUtil.isEmpty(userEntity)) {return ResponseUtil.resp("没有查到对用用户信息", CodeEnum.FAIL);}CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver(request.getServletContext());if (!multipartResolver.isMultipart(request)) {return ResponseUtil.resp("没有任何文件被提交", CodeEnum.FAIL);}List<MultipartFile> fileList = new ArrayList<>();fileList.addAll(request.getFiles("file"));fileList.addAll(request.getFiles("userPortrait"));long totalStartTimeMillis = System.currentTimeMillis();for (int i = 0; i < fileList.size(); i++) {long startTimeMillis = System.currentTimeMillis();MultipartFile multipartFile = fileList.get(i);if (multipartFile != null) {String originalFilename = multipartFile.getOriginalFilename();String filePath = CacheEnum.getFileUploadPath() + multipartFile.getName() + "/" + originalFilename;File localFile = new File(filePath);FileUtil.createOrExistsFile(localFile);multipartFile.transferTo(localFile);LogTrack.w(filePath + " 耗时  " + (System.currentTimeMillis() - startTimeMillis) + " 毫秒");}}LogTrack.w("所有文件耗时  " + (System.currentTimeMillis() - totalStartTimeMillis) + " 毫秒");return ResponseUtil.resp("上传成功", CodeEnum.SUCCESS);}}

文件下载

ApiService
    @FormUrlEncoded@Streaming@POST("downloadFile")fun downloadFile(@FieldMap params: Map<String, String>): Observable<ResponseBody>
RetrofitUtil
public class RetrofitUtil {private static Map<String, Retrofit> retrofitMap = new HashMap<>();private static Retrofit.Builder getRetrofitBuilder(String baseUrl, OnUploadListener onUploadListener, OnDownloadListener onDownloadListener) {OkHttpClient.Builder okHttpBuilder = new OkHttpClient.Builder();okHttpBuilder.connectTimeout(HttpEnum.connectTimeout, TimeUnit.MILLISECONDS);okHttpBuilder.readTimeout(HttpEnum.readTimeout, TimeUnit.MILLISECONDS);okHttpBuilder.writeTimeout(HttpEnum.writeTimeout, TimeUnit.MILLISECONDS);okHttpBuilder.retryOnConnectionFailure(true);HttpsUtil.sslSocketFactory(okHttpBuilder, BaseUtil.getInstance().application(), "hack.cer");okHttpBuilder.addInterceptor(new ParamsInterceptor(new SimpleParamsProvider()));okHttpBuilder.addInterceptor(HttpLogInterceptor.getInstance());if (onUploadListener != null) {okHttpBuilder.addInterceptor(new UploadProgressInterceptor().setOnUploadListener(onUploadListener));}if (onDownloadListener != null) {okHttpBuilder.addInterceptor(new DownloadProgressInterceptor().setOnDownloadListener(onDownloadListener));}Retrofit.Builder retrofitBuilder = new Retrofit.Builder().baseUrl(baseUrl).client(okHttpBuilder.build());retrofitBuilder.addCallAdapterFactory(RxJava2CallAdapterFactory.create());if (onDownloadListener == null) {retrofitBuilder.addConverterFactory(GsonConverterFactory.create());}return retrofitBuilder;}public static ApiService getService() {return getRetrofit(UrlEnum.baseApiUrl, null, null).create(ApiService.class);}public static ApiService getService(OnUploadListener onUploadListener, OnDownloadListener onDownloadListener) {return getRetrofit(UrlEnum.baseApiUrl, onUploadListener, onDownloadListener).create(ApiService.class);}public static ApiService getService(String baseUrl, OnUploadListener onUploadListener, OnDownloadListener onDownloadListener) {return getRetrofit(baseUrl, onUploadListener, onDownloadListener).create(ApiService.class);}public static <T> T getService(Class<T> service, OnUploadListener onUploadListener, OnDownloadListener onDownloadListener) {return getRetrofit(UrlEnum.baseApiUrl, onUploadListener, onDownloadListener).create(service);}public static <T> T getService(String baseUrl, Class<T> service, OnUploadListener onUploadListener, OnDownloadListener onDownloadListener) {return getRetrofit(baseUrl, onUploadListener, onDownloadListener).create(service);}private static Retrofit getRetrofit(String baseUrl, OnUploadListener onUploadListener, OnDownloadListener onDownloadListener) {StringBuilder retrofitKeyBuilder = new StringBuilder();if (baseUrl != null) {retrofitKeyBuilder.append(baseUrl);}if (onDownloadListener != null) {retrofitKeyBuilder.append(onDownloadListener.getClass().getSimpleName());}if (onUploadListener != null) {retrofitKeyBuilder.append(onUploadListener.getClass().getSimpleName());}Retrofit retrofit = retrofitMap.get(retrofitKeyBuilder.toString());if (retrofit != null) {return retrofit;}retrofit = getRetrofitBuilder(baseUrl, onUploadListener, onDownloadListener).build();retrofitMap.put(retrofitKeyBuilder.toString(), retrofit);return retrofit;}}
DownloadProgressInterceptor
public class DownloadProgressInterceptor implements Interceptor {private OnDownloadListener onDownloadListener;public DownloadProgressInterceptor setOnDownloadListener(OnDownloadListener onDownloadListener) {this.onDownloadListener = onDownloadListener;return this;}@Overridepublic Response intercept(Chain chain) throws IOException {Response originalResponse = chain.proceed(chain.request());if (onDownloadListener == null) {return originalResponse;}return originalResponse.newBuilder().body(new ProgressResponseBody(originalResponse.body()).setOnDownloadListener(onDownloadListener)).build();}
}
ProgressResponseBody
public class ProgressResponseBody extends ResponseBody {private final ResponseBody responseBody;private OnDownloadListener onDownloadListener;private BufferedSource bufferedSource;ProgressResponseBody(ResponseBody responseBody) {this.responseBody = responseBody;}ProgressResponseBody setOnDownloadListener(OnDownloadListener listener) {this.onDownloadListener = listener;return this;}@Overridepublic MediaType contentType() {return responseBody.contentType();}@Overridepublic long contentLength() {return responseBody.contentLength();}@Overridepublic BufferedSource source() {if (null == bufferedSource) {bufferedSource = Okio.buffer(source(responseBody.source()));}return bufferedSource;}private Source source(Source source) {return new ForwardingSource(source) {/**当前 下载 进度*/long currLength = 0L;@Overridepublic long read(Buffer sink, long byteCount) throws IOException {long bytesRead = super.read(sink, byteCount);currLength += bytesRead != -1 ? bytesRead : 0;double progress = 100 * ((double) currLength) / ((double) responseBody.contentLength());String doubleFormat = decimalFormat(progress + "", 2, "1.00");//LogTrack.w("contentLength = "+responseBody.contentLength()+"  currLength = "+currLength+"  progress = "+doubleFormat);BaseUtil.getInstance().mainHandler().post(new Runnable() {@Overridepublic void run() {onDownloadListener.onDownloadProgress(responseBody.contentLength(), currLength, doubleFormat);}});return bytesRead;}};}private String decimalFormat(String sourceNum) {return decimalFormat(sourceNum, 2, "0.00");}private String decimalFormat(String sourceNum, String defaultValue) {return decimalFormat(sourceNum, 2, defaultValue);}private String decimalFormat(String sourceNum, int length, String defaultValue) {if (isEmpty(sourceNum)) {return defaultValue;}char firstChar = sourceNum.charAt(0);if (sourceNum.charAt(0) < '0' || firstChar > '9') {return defaultValue;}StringBuilder trailBuilder = new StringBuilder();for (int i = 0; i < length + 2; i++) {trailBuilder.append("0");}int indexDot = sourceNum.indexOf('.');if (indexDot >= 0) {sourceNum += trailBuilder.toString();}if (indexDot < 0) {indexDot = 1;sourceNum += ("." + trailBuilder.toString());}return sourceNum.substring(0, indexDot + length + 1);}private boolean isEmpty(Object text) {return text == null || text.toString().length() <= 0;}private boolean isNotEmpty(Object text) {return !isEmpty(text);}
}
DownloadPresenter

class DownloadModel : DownloadContract.Model {override fun downloadTextFile(params: Map<String, String>, onDownloadListener: OnDownloadListener): Observable<ResponseBody> =RetrofitUtil.getService(null, onDownloadListener).downloadFile(params)
}class DownloadPresenter(view: DownloadContract.View) : AbsPresenter<DownloadContract.View, DownloadContract.Model>(view), DownloadContract.Presenter {/*** 生成 数据模型*/override fun createModel() = DownloadModel()@Suppress("ObjectLiteralToLambda")override fun doDownloadFile(phone: String, fileType: String) {var filename = "RecyclerView.java"if ("text".equals(fileType, ignoreCase = true)) {filename = "RecyclerView.java"}if ("image".equals(fileType, ignoreCase = true)) {filename = "黄喉蜂虎.png"}if ("video".equals(fileType, ignoreCase = true)) {filename = "UI设计.mp4"}val params = hashMapOf<String, String>("fileType" to fileType, "phone" to phone)model.downloadTextFile(params, OnDownloadListener { countLength, currLength, progress ->"$countLength  $currLength  $progress".logW()view.onUploadProgress(progress)}).map {DownloadHelper.getInstance().diskFilePath(CacheEnum.cachePath + filename).saveSync(it.byteStream())}.compose(RxHelper.defaultTransformer(view)).subscribe(object : LiteObserver<Any>() {override fun onNext(result: Any) {LogTrack.e(result)}})}
}
DownloadHelper
public class DownloadHelper {private static DownloadHelper instance;private String filePath;private DownloadHelper() {}public static DownloadHelper getInstance() {if (instance == null) {synchronized (DownloadHelper.class) {instance = instance == null ? new DownloadHelper() : instance;}}return instance;}/*** @param filePath 文件 要存储在 SD 卡的 目标路径(必须携带SD卡的根目录),无论源文件是否存在, 都是清空写入*/public DownloadHelper diskFilePath(String filePath) {this.filePath = filePath;createOrExistsFile(filePath);return this;}public void saveAsync(InputStream inputStream) {new Thread() {boolean saveFinish = false;@Overridepublic void run() {while (!saveFinish) {saveFinish = saveSync(inputStream);try {sleep(10);} catch (InterruptedException e) {e.printStackTrace();}}}}.start();}public boolean saveSync(InputStream inputStream) {/**此处 获取 inputStream 的 .available()  是没有意义的*/File file = new File(filePath);FileOutputStream out = null;try {out = new FileOutputStream(file);
//            LogTrack.e(file.getAbsolutePath() + "  文件 大小 = " + FileUtil.byte2FitSize());byte[] buffer = new byte[1024 * 128];int len;long currCount = 0;while ((len = inputStream.read(buffer)) != -1) {currCount += len;//LogTrack.e("读取 " + FileUtil.byte2FitSize(currCount));out.write(buffer, 0, len);}out.flush();} catch (FileNotFoundException e) {LogTrack.e(e);} catch (IOException e) {e.printStackTrace();} finally {try {if (out != null) {out.close();}} catch (IOException e) {}try {if (inputStream != null) {inputStream.close();}} catch (IOException e) {}}return true;}/*** 判断文件是否存在,不存在则判断是否创建成功** @param filePath 文件路径* @return {@code true}: 存在或创建成功<br>{@code false}: 不存在或创建失败*/private boolean createOrExistsFile(String filePath) {return createOrExistsFile(getFile(filePath));}/*** 判断文件是否存在,不存在则判断是否创建成功** @param file 文件* @return {@code true}: 存在或创建成功<br>{@code false}: 不存在或创建失败*/private boolean createOrExistsFile(File file) {if (file == null) return false;// 如果存在,是文件则返回true,是目录则返回falseif (file.exists()) return file.isFile();if (!mkdirs(file.getParentFile())) return false;try {return file.createNewFile();} catch (IOException e) {e.printStackTrace();return false;}}/*** 判断目录是否存在,不存在则判断是否创建成功** @param file 文件* @return {@code true}: 存在或创建成功<br>{@code false}: 不存在或创建失败*/private boolean mkdirs(File file) {// 如果存在,是目录则返回true,是文件则返回false,不存在则返回是否创建成功return file != null && (file.exists() ? file.isDirectory() : file.mkdirs());}/*** 根据文件路径获取文件** @param filePath 文件路径* @return 文件*/private File getFile(String filePath) {return new File(filePath);}
}

后端 遇到的问题

ClientAbortException
org.apache.catalina.connector.ClientAbortException: java.io.IOException: 你的主机中的软件中止了一个已建立的连接。at org.apache.catalina.connector.OutputBuffer.realWriteBytes(OutputBuffer.java:356)at org.apache.catalina.connector.OutputBuffer.appendByteArray(OutputBuffer.java:785)at org.apache.catalina.connector.OutputBuffer.append(OutputBuffer.java:714)at org.apache.catalina.connector.OutputBuffer.writeBytes(OutputBuffer.java:391)at org.apache.catalina.connector.OutputBuffer.write(OutputBuffer.java:369)at org.apache.catalina.connector.CoyoteOutputStream.write(CoyoteOutputStream.java:96)at com.alex.appserver.module.multipartfile.MultipartFileController.downloadTextFile(MultipartFileController.java:99)at com.alex.appserver.module.multipartfile.MultipartFileController$$FastClassBySpringCGLIB$$f6856409.invoke(<generated>)at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:738)at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)at org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint.proceed(MethodInvocationProceedingJoinPoint.java:85)at com.alex.appserver.interceptor.PrintParamsInterceptorController.interceptor(PrintParamsInterceptorController.java:49)at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)at java.lang.reflect.Method.invoke(Method.java:497)at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:629)at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:618)at org.springframework.aop.aspectj.AspectJAroundAdvice.invoke(AspectJAroundAdvice.java:70)at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:168)at org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint.proceed(MethodInvocationProceedingJoinPoint.java:85)at com.alex.appserver.interceptor.FormWholeCheckInterceptorController.formWholeInterceptor(FormWholeCheckInterceptorController.java:80)at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)at java.lang.reflect.Method.invoke(Method.java:497)at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:629)at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:618)at org.springframework.aop.aspectj.AspectJAroundAdvice.invoke(AspectJAroundAdvice.java:70)at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:168)at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:673)at com.alex.appserver.module.multipartfile.MultipartFileController$$EnhancerBySpringCGLIB$$1ce62f07.downloadTextFile(<generated>)
.....
....
怎么解决

Generally, you can just ignore it. This exception will be thrown when the client has abruptly aborted the HTTP request while the page is still loading.
This will occur when the client pressed Esc, or hastily navigated away, or closed the browser, or got network outage, or even caught fire. All of this is totally out your control.

byte[] buff = new byte[16 * 1024];
BufferedInputStream bufferedInputStream = null;
OutputStream outputStream;
try {outputStream = response.getOutputStream();bufferedInputStream = new BufferedInputStream(new FileInputStream(file));LogTrack.e(file.getAbsolutePath() + "  文件 大小 = " + FileUtil.byte2FitSize(file.length()));int charResult = bufferedInputStream.read(buff);long currCount = 0;while (charResult != -1) {currCount += buff.length;LogTrack.e("读取 " + FileUtil.byte2FitSize(currCount));try {outputStream.write(buff, 0, buff.length);} catch (ClientAbortException ex) {}try {outputStream.flush();} catch (ClientAbortException ex) {}charResult = bufferedInputStream.read(buff);}
} catch (ClientAbortException ex) {LogTrack.e("不关心 ClientAbortException");ex.printStackTrace();
} catch (Exception e) {e.printStackTrace();
} finally {if (bufferedInputStream != null) {try {bufferedInputStream.close();} catch (IOException e) {e.printStackTrace();}}
}
响应报文头中包含中文, 但是乱码
response.setContentType(ContentType.application_octet_stream);
response.setHeader("Accept-Ranges", "bytes");
response.setHeader("Content-Length", String.valueOf(file.length()));
/*** 解决 响应头  中文 乱码问题, 此时 前端 解析 响应体 编码集 必须使用 UTF-8* response.setHeader("Content-Disposition", "attachment;filename=" + new String(filename.getBytes("UTF-8"), "ISO8859-1"));* 第一个   UTF-8    对应 当前 编辑器的  编码集* 第二个 ISO8859-1  为什么是这样? 我自己也不知道* */
response.setHeader("Content-Disposition", "attachment;filename=" + new String(filename.getBytes("UTF-8"), "ISO8859-1"));
response.setCharacterEncoding("UTF-8");
Paste_Image.png

文件上传、文件下载与进度回调相关推荐

  1. springboot:实现文件上传下载实时进度条功能【附带源码】

    0. 引言 记得刚入行的时候,做了一个文件上传的功能,因为上传时间较久,为了用户友好性,想要添加一个实时进度条,显示进度.奈何当时技术有限,查了许久也没用找到解决方案,最后不了了之. 近来偶然想到这个 ...

  2. SpringMVC +layui 实现多文件上传,附加进度条

    首先在进行文件上传操作的时候,你得对layui 中文件上传的一些参数先了解一下,不要网上复制粘贴,起码你要对layui 文件上传的必备参数做一些大致掌握,也是自己学习. (1) 首先你得引入layui ...

  3. java mvc上传文件进度_java相关:springMVC+ajax实现文件上传且带进度条实例

    java相关:springMVC+ajax实现文件上传且带进度条实例 发布于 2020-7-5| 复制链接 本篇文章主要介绍了springMVC+ajax实现文件上传且带进度条实例,具有一定的参考价值 ...

  4. java 上传 进度,关于 javaweb的文件上传实时显示进度

    方法:使用单例保存实时信息.具体的实现方法就是,当用户点击了处理按钮时,在后台开启一个线程进行处理,并且每进行到一步,就向单例中写入当前状态信息.然后编写一个servlet,用于返回单例中的信息,前台 ...

  5. Spring MVC 文件上传 文件下载

    索引: 目录索引 参看代码 GitHub: pom.xml WebConfig.java index.jsp upload.jsp FileUploadController.java Files_Ut ...

  6. Asp.Net实现无刷新文件上传并显示进度条(非服务器控件实现)

    相信通过Asp.Net的服务器控件上传文件在简单不过了,通过AjaxToolkit控件实现上传进度也不是什么难事,为什么还要自己辛辛苦苦来实现呢?我并不否认"拿来主义",只是我个人 ...

  7. [WebApi]WebApi通过接口上传文件-单文件 多文件上传 文件下载

    WebApi通过接口上传文件 单文件上传(ajax,Form表单都适用) 1.html 2.javascript 3.C# Form表单之单文件上传 1.html 2.javascript 3.C# ...

  8. Jmeter接口测试-文件上传/文件下载

    目录 1.前言 2.文件上传 3.文件下载 1.前言 对于大多数被测接口的请求方式,使用Jmeter是完全可以的,但是类似文件上传与下载的接口请求方式,使用起来没有在Postman上方便,需要一些特殊 ...

  9. CI框架文件上传+多文件上传+文件下载

    代码中的logMessage()和Directory()是自己封装的记录日志和递归创建文件夹助手函数 注意修改!!! 点击查看:Directory()    logMessage() <?php ...

  10. vue获取上传进度_vue通过input选取apk文件上传,显示进度条

    选择文件 type="file"accept=".ipa,.apk"name="upload"id="file"@cha ...

最新文章

  1. 无向图的直径以及树的直径
  2. PHP - PDO 之 mysql 参数绑定
  3. 服务禁止方法_Linux禁止ping以及开启ping的方法
  4. html获取contextpath,JavaScript中${pageContext.request.contextPath}取值问题及解决方案
  5. 【Code-Snippet】ProgressBar
  6. 004、SVN更新改密码
  7. springboot报错Table 'wechat.hibernate_sequence' doesn't exist
  8. python怎么用for循环找出最大值_从“for in”循环中获取最小值和最大值
  9. 实战HTML:根据参数构造动态设备监测列表
  10. 【电脑硬件问题】视频接口和显示器偏色
  11. 小D课堂-SpringBoot 2.x微信支付在线教育网站项目实战_3-4.动态Sql语句Mybaties SqlProvider...
  12. 局域网聊天服务器(openfire)安装与配置
  13. 【小技巧】PhotoShop + lllustrator制作电子签名矢量图
  14. 二级计算机中一级标题设置,如何编辑目录中一级标题二级标题的不同格式
  15. 【工商银行科技菁英计划笔试】压缩字符串
  16. 推荐几本大学生必看的书单
  17. 制作flash动画心得
  18. 季琦:VC青睐的“攻城略地者”
  19. webpack-theme-color-replacer自定义element-ui主题
  20. fix-下拉出现白条问题

热门文章

  1. 我的世界java版月步教程,【我的世界】我的世界三大极限操作展现!完美月步艳惊四座 无道具瞬移实现_玩得好游戏攻略...
  2. c语言1000的阶乘有几个零,1000的阶乘后面有多少个零?
  3. 商品详情页公司产品画册企业宣传页面怎么设计和制作呢?
  4. 计算机制作母亲节,【计算机·节日】纪念母亲节——母爱神圣
  5. 2021年研究生数模B题论文记录
  6. 微信小程序网悦新闻开发--视频模块开发(四)
  7. MySQL 数据库连接使用
  8. libstdc++.so.6
  9. JUC--006--locks2
  10. 拿下一级造价工程师证书,可以从事什么工作?