我需要使用 Retrofit 库在我的应用中下载所有类型的文件(二进制、图像、文本等)。网上的所有示例都使用 HTML GET 方法。我需要使用 POST 方法来防止自动缓存。
我的问题是如何使用 POST 方法在 Retrofit 中下载文件?
我的问题是如何使用 POST 方法在 Retrofit 中下载文件?
在 kotlin
中,做如下操作:
在您的服务中添加方法:
@Streaming
@GET
suspend fun downloadFile(@Url fileUrl:String): Response<ResponseBody>
在ViewModel中调用此方法:
viewModelScope.launch {
val responseBody=yourServiceInstance.downloadFile(url).body()
saveFile(responseBody,pathWhereYouWantToSaveFile)
}
保存文件:
fun saveFile(body: ResponseBody?, pathWhereYouWantToSaveFile: String):String{
if (body==null)
return ""
var input: InputStream? = null
try {
input = body.byteStream()
//val file = File(getCacheDir(), "cacheFileAppeal.srl")
val fos = FileOutputStream(pathWhereYouWantToSaveFile)
fos.use { output ->
val buffer = ByteArray(4 * 1024) // or other buffer size
var read: Int
while (input.read(buffer).also { read = it } != -1) {
output.write(buffer, 0, read)
}
output.flush()
}
return pathWhereYouWantToSaveFile
}catch (e:Exception){
Log.e("saveFile",e.toString())
}
finally {
input?.close()
}
return ""
}
注意:
refrofit
客户端的基本 url 和传递给 downloadFile 的 url 是有效的文件 url:Retrofit 的基本 url + downloadFile 方法的 url = 文件 url
在 downloadFile
前面加上 suspend 关键字,以便从 ViewModel 调用此方法,我使用了 viewModelScope.launch {}
,您可以根据您的调用端使用不同的协程作用域。
现在是 pathWhereYouWantToSaveFile
,如果您想将文件存储到项目的文件目录中,可以这样做:
val fileName=url.substring(url.lastIndexOf("/")+1)
val pathWhereYouWantToSaveFile = myApplication.filesDir.absolutePath+fileName
如果您将下载的文件存储在文件夹或缓存目录下,则无需获取权限,否则对于公共存储,您知道流程。
使用 @Streaming
异步
编辑1
//On your api interface
@POST("path/to/your/resource")
@Streaming
void apiRequest(Callback<POJO> callback);
restAdapter.apiRequest(new Callback<POJO>() {
@Override
public void success(POJO pojo, Response response) {
try {
//you can now get your file in the InputStream
InputStream is = response.getBody().in();
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void failure(RetrofitError error) {
}
});
同步的
//On your api interface
@POST("path/to/your/resource")
@Streaming
Response apiRequest();
Response response = restAdapter.apiRequest();
try {
//you can now get your file in the InputStream
InputStream is = response.getBody().in();
} catch (IOException e) {
e.printStackTrace();
}
Object
。 - NaviRamylepublic interface ServerAPI {
@GET
Call<ResponseBody> downlload(@Url String fileUrl);
Retrofit retrofit =
new Retrofit.Builder()
.baseUrl("http://192.168.43.135/retro/") // REMEMBER TO END with /
.addConverterFactory(GsonConverterFactory.create())
.build();
}
//How To Call
public void download(){
ServerAPI api = ServerAPI.retrofit.create(ServerAPI.class);
api.downlload("http://192.168.43.135/retro/pic.jpg").enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
try {
File path = Environment.getExternalStorageDirectory();
File file = new File(path, "file_name.jpg");
FileOutputStream fileOutputStream = new FileOutputStream(file);
IOUtils.write(response.body().bytes(), fileOutputStream);
}
catch (Exception ex){
}
}
@Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
}
});
}
使用 Kotlin,它会变得更加简单。
API 服务
@GET
@Streaming
fun download(@Url url: String): Call<ResponseBody>
API客户端
object ApiClient {
private val retrofit = ...
val service: ApiService = retrofit.create(ApiService::class.java)
}
下载功能
fun download(urlString: String, target: File) {
val response = ApiClient.service.download(urlString).execute()
response.body()?.byteStream()?.use {
target.parentFile?.mkdirs()
FileOutputStream(target).use { targetOutputStream ->
it.copyTo(targetOutputStream)
}
} ?: throw RuntimeException("failed to download: $urlString")
}
okhttp3.ResponseBody
来接收原始二进制数据,而不是任何POJO对象。POST
方法获取文件,很容易,只需将@GET
更改为@POST
,但它取决于您的服务器是否支持POST
方法!您可以使用以下代码以带有进度的方式进行下载(Kotlin)
Retrofit Api服务
@Streaming
@GET
fun downloadFile(@Url fileUrl: String): Observable<Response<ResponseBody>>
请确保在下载大文件时添加@Streaming
注释。
然后将以下代码粘贴到您的Activity或Fragment中。
fun downloadfileFromRetrofit() {
val retrofit = Retrofit.Builder()
.baseUrl("ENTER_YOUR_BASE_URL")
.client(OkHttpClient.Builder().build())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create()).build()
val downloadService = retrofit.create(RetrofitApi::class.java)
downloadService.downloadFile("FILE_URL_PATH").observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io()).subscribe({
val task = object : AsyncTask<Void, Integer, Void>() {
override fun doInBackground(vararg voids: Void): Void? {
val writtenToDisk =writeResponseBodyToDisk(it.body()!!)
println("file download was a success? $writtenToDisk")
return null
}
}
task.execute()
}, {
print(it.message)
})
}
fun writeResponseBodyToDisk(body: ResponseBody): Boolean {
val appDirectoryName = "YOUR_DIRECTORY_NAME"
val filename = "YOUR_FILE_NAME"
val apkFile = File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), filename)
try {
var inputStream: InputStream? = null
var outputStream: OutputStream? = null
try {
val fileReader = ByteArray(4096)
val fileSize = body.contentLength()
var fileSizeDownloaded: Long = 0
inputStream = body.byteStream()
outputStream = FileOutputStream(apkFile)
while (true) {
val read = inputStream!!.read(fileReader)
if (read == -1) {
break
}
outputStream.write(fileReader, 0, read)
fileSizeDownloaded += read.toLong()
calulateProgress(fileSize.toDouble(),fileSizeDownloaded.toDouble()
println("file downloading $fileSizeDownloaded of $fileSize")
outputStream.flush()
return true
} catch (e: Exception) {
println(e.toString())
return false
} finally {
if (inputStream != null) {
inputStream!!.close()
}
outputStream?.close()
}
} catch (e: Exception) {
println(e.toString())
return false
}
}
fun calulateProgress(totalSize:Double,downloadSize:Double):Double{
return ((downloadSize/totalSize)*100)
}
File file = new File("Your_File_path/name");
private void startDownload() {
if (!NetWorkUtils.getInstance(context).isNetworkAvailable()) {
Toast.makeText(context, "No data connection available", Toast.LENGTH_SHORT).show();
return;
}
showProgressDialog();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(FILE_BASE_URL)
.build();
FileHandlerService handlerService = retrofit.create(FileHandlerService.class);
Call<ResponseBody> call = handlerService.downloadFile(mFileName);
call.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
dismissProgressDialog();
if (response.isSuccessful()) {
if (writeResponseBodyToDisk(response.body())) {
listener.onFileLoaded(file);
}
} else {
listener.onDownloadFailed("Resource not Found");
}
}
@Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
dismissProgressDialog();
listener.onDownloadFailed("Download Failed");
t.printStackTrace();
}
});
}
interface FileHandlerService {
@GET("uploads/documents/{file_name}")
Call<ResponseBody> downloadFile(
@Path("file_name") String imageName);
}
private boolean writeResponseBodyToDisk(ResponseBody body) {
try {
InputStream inputStream = null;
OutputStream outputStream = null;
try {
byte[] fileReader = new byte[4096];
long fileSize = body.contentLength();
long fileSizeDownloaded = 0;
inputStream = body.byteStream();
outputStream = new FileOutputStream(file);
while (true) {
int read = inputStream.read(fileReader);
if (read == -1) {
break;
}
outputStream.write(fileReader, 0, read);
fileSizeDownloaded += read;
Log.d(TAG, "file download: " + fileSizeDownloaded + " of " + fileSize);
}
outputStream.flush();
return true;
} catch (IOException e) {
e.printStackTrace();
return false;
} finally {
if (inputStream != null) {
inputStream.close();
}
if (outputStream != null) {
outputStream.close();
}
}
} catch (IOException e) {
e.printStackTrace();
return false;
}
}
void getRetrofitImage() {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(url)
.addConverterFactory(GsonConverterFactory.create())
.build();
RetrofitImageAPI service = retrofit.create(RetrofitImageAPI.class);
Call<ResponseBody> call = service.getImageDetails();
call.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Response<ResponseBody> response, Retrofit retrofit) {
try {
Log.d("onResponse", "Response came from server");
boolean FileDownloaded = DownloadImage(response.body());
Log.d("onResponse", "Image is downloaded and saved ? " + FileDownloaded);
} catch (Exception e) {
Log.d("onResponse", "There is an error");
e.printStackTrace();
}
}
@Override
public void onFailure(Throwable t) {
Log.d("onFailure", t.toString());
}
});
}
private boolean DownloadImage(ResponseBody body) {
try {
Log.d("DownloadImage", "Reading and writing file");
InputStream in = null;
FileOutputStream out = null;
try {
in = body.byteStream();
out = new FileOutputStream(getExternalFilesDir(null) + File.separator + "AndroidTutorialPoint.jpg");
int c;
while ((c = in.read()) != -1) {
out.write(c);
}
}
catch (IOException e) {
Log.d("DownloadImage",e.toString());
return false;
}
finally {
if (in != null) {
in.close();
}
if (out != null) {
out.close();
}
}
int width, height;
ImageView image = (ImageView) findViewById(R.id.imageViewId);
Bitmap bMap = BitmapFactory.decodeFile(getExternalFilesDir(null) + File.separator + "AndroidTutorialPoint.jpg");
width = 2*bMap.getWidth();
height = 6*bMap.getHeight();
Bitmap bMap2 = Bitmap.createScaledBitmap(bMap, width, height, false);
image.setImageBitmap(bMap2);
return true;
} catch (IOException e) {
Log.d("DownloadImage",e.toString());
return false;
}
}
下载文件的请求声明如下所示
// option 1: a resource relative to your base URL
@GET("/resource/example.zip")
Call<ResponseBody> downloadFileWithFixedUrl();
// option 2: using a dynamic URL
@GET
Call<ResponseBody> downloadFileWithDynamicUrlSync(@Url String fileUrl);
在声明请求调用后,像这样
FileDownloadService downloadService = ServiceGenerator.create(FileDownloadService.class);
Call<ResponseBody> call = downloadService.downloadFileWithDynamicUrlSync(fileUrl);
call.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
if (response.isSuccess()) {
Log.d(TAG, "server contacted and has file");
boolean writeToDisk = writeToDisk(response.body());
Log.d(TAG, "file downloaded " + writtenToDisk);
} else {
Log.d(TAG, "server error");
}
}
@Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
Log.e(TAG, "error");
}
});
同时将此方法实现为将文件保存到您的SD卡中。
private boolean writeToDisk(ResponseBody body) {
try { File mediaStorageDir = new File(
Environment
.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES),
"ProfileImage");
// Create the storage directory if it does not exist
if (!mediaStorageDir.exists()) {
if (!mediaStorageDir.mkdirs()) {
Log.e("ProfileImage", "Oops! Failed create "
+ "ProfileImage" + " directory");
}
}
File futureStudioIconFile = new File(mediaStorageDir.getPath() + File.separator
+ "userImage.png");
InputStream inputStream = null;
OutputStream outputStream = null;
try {
byte[] fileReader = new byte[4096];
long fileSize = body.contentLength();
long fileSizeDownloaded = 0;
inputStream = body.byteStream();
outputStream = new FileOutputStream(futureStudioIconFile);
while (true) {
int read = inputStream.read(fileReader);
if (read == -1) {
break;
}
outputStream.write(fileReader, 0, read);
fileSizeDownloaded += read;
Log.d(TAG, "file download: " + fileSizeDownloaded + " of " + fileSize);
}
outputStream.flush();
return true;
} catch (IOException e) {
return false;
} finally {
if (inputStream != null) {
inputStream.close();
}
if (outputStream != null) {
outputStream.close();
}
}
} catch (IOException e) {
return false;
}
}