如何使用 Retrofit2 下载文件?
How to download a file with Retrofit2?
如何从我的PHP下载一个文件(image/video) 服务器使用 Retrofit2 ?
我无法在网上找到任何关于如何继续的资源或教程;我发现 处理 SO 上的某个下载错误,但我不是很清楚。谁能指出我正确的方向?
更新:
这是我的代码:
FileDownloadService.java
public interface FileDownloadService {
@GET(Constants.UPLOADS_DIRECTORY + "/{filename}")
@Streaming
Call<ResponseBody> downloadRetrofit(@Path("filename") String fileName);
}
MainActivity.java(@Blackbelt的解决方案)
private void downloadFile(String filename) {
FileDownloadService service = ServiceGenerator
.createService(FileDownloadService.class, Constants.SERVER_IP_ADDRESS);
Call<ResponseBody> call = service.downloadRetrofit("db90408a4bb1ee65d3e09d261494a49f.jpg");
call.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(final Response<ResponseBody> response, Retrofit retrofit) {
try {
InputStream is = response.body().byteStream();
FileOutputStream fos = new FileOutputStream(
new File(Environment.getExternalStorageDirectory(), "image.jpg")
);
int read = 0;
byte[] buffer = new byte[32768];
while ((read = is.read(buffer)) > 0) {
fos.write(buffer, 0, read);
}
fos.close();
is.close();
} catch (Exception e) {
Toast.makeText(MainActivity.this, "Exception: " + e.toString(), Toast.LENGTH_LONG).show();
}
}
@Override
public void onFailure(Throwable t) {
Toast.makeText(MainActivity.this, "Failed to download file...", Toast.LENGTH_LONG).show();
}
});
}
当 USB 调试处于活动状态时,我得到一个 FileNotFoundException,而不是 NetworkOnMainThreadException。
MainActivity.java:(@Emanuel的解决方案)
private void downloadFile(String filename) {
FileDownloadService service = ServiceGenerator
.createService(FileDownloadService.class, Constants.SERVER_IP_ADDRESS);
Call<ResponseBody> call = service.downloadRetrofit("db90408a4bb1ee65d3e09d261494a49f.jpg");
call.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(final Response<ResponseBody> response, Retrofit retrofit) {
Log.i(TAG, "external storage = " + (Environment.getExternalStorageState() == null));
Toast.makeText(MainActivity.this, "Downloading file... " + Environment.getExternalStorageDirectory(), Toast.LENGTH_LONG).show();
File file = new File(Environment.getDataDirectory().toString() + "/aouf/image.jpg");
try {
file.createNewFile();
Files.asByteSink(file).write(response.body().bytes());
} catch (Exception e) {
Toast.makeText(MainActivity.this,
"Exception: " + e.toString(),
Toast.LENGTH_LONG).show();
}
}
@Override
public void onFailure(Throwable t) {
Toast.makeText(MainActivity.this, "Failed to download file...", Toast.LENGTH_LONG).show();
}
});
}
我得到一个 FileNotFoundException。
要下载 文件,您可能需要响应的原始 InputStream
并写入 sdcard
上的内容。为此,对于 return 类型 Call<ResponseBody>
,您应该使用 ResponseBody 作为 T
。然后,您将使用 Retrofit
到 enqueue
一个
Callback<ResponseBody>
当 onResponse
@Override
public void onResponse(final Response<ResponseBody> response, Retrofit retrofit) {
被调用,你可以检索InputStream
,用response.byteStream()
,从中读取,并将你读取的内容写入sdcard(看看here)
这是一个展示如何下载 Retrofit JAR 文件的小例子。您可以根据自己的需要进行调整。
这是界面:
import com.squareup.okhttp.ResponseBody;
import retrofit.Call;
import retrofit.http.GET;
import retrofit.http.Path;
interface RetrofitDownload {
@GET("/maven2/com/squareup/retrofit/retrofit/2.0.0-beta2/{fileName}")
Call<ResponseBody> downloadRetrofit(@Path("fileName") String fileName);
}
这是一个Javaclass使用界面:
import com.google.common.io.Files;
import com.squareup.okhttp.ResponseBody;
import retrofit.Call;
import retrofit.Callback;
import retrofit.Response;
import retrofit.Retrofit;
import java.io.File;
import java.io.IOException;
public class Main {
public static void main(String... args) {
Retrofit retrofit = new Retrofit.Builder().
baseUrl("http://repo1.maven.org").
build();
RetrofitDownload retrofitDownload = retrofit.create(RetrofitDownload.class);
Call<ResponseBody> call = retrofitDownload.downloadRetrofit("retrofit-2.0.0-beta2.jar");
call.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Response<ResponseBody> response, Retrofit retrofitParam) {
File file = new File("retrofit-2.0.0-beta2.jar");
try {
file.createNewFile();
Files.asByteSink(file).write(response.body().bytes());
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void onFailure(Throwable t) {
}
});
}
}
如果有人无意中发现了这个响应,这就是我将改造与 Rx 结合使用的方式。每个下载的文件都会被缓存,任何具有相同 url 的后续请求都将 return 已下载的文件。
为了使用它,只需订阅这个 observable 并传递你的 url。这会将您的文件保存在下载目录中,因此如果您的应用面向 API 23 或更高版本,请确保请求权限。
public Observable<File> getFile(final String filepath) {
URL url = null;
try {
url = new URL(filepath);
} catch (MalformedURLException e) {
e.printStackTrace();
}
final String name = url.getPath().substring(url.getPath().lastIndexOf("/") + 1);
final File file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), name);
if (file.exists()) {
return Observable.just(file);
} else {
return mRemoteService.getFile(filepath).flatMap(new Func1<Response<ResponseBody>, Observable<File>>() {
@Override
public Observable<File> call(final Response<ResponseBody> responseBodyResponse) {
return Observable.create(new Observable.OnSubscribe<File>() {
@Override
public void call(Subscriber<? super File> subscriber) {
try {
final File file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getAbsoluteFile(), name);
BufferedSink sink = Okio.buffer(Okio.sink(file));
sink.writeAll(responseBodyResponse.body().source());
sink.flush();
sink.close();
subscriber.onNext(file);
subscriber.onCompleted();
file.deleteOnExit();
} catch (IOException e) {
Timber.e("Save pdf failed with error %s", e.getMessage());
subscriber.onError(e);
}
}
});
}
});
}
}
改造部分调用
@Streaming
@GET
Observable<retrofit2.Response<ResponseBody>> getFile(@Url String fileUrl);
如何从我的PHP下载一个文件(image/video) 服务器使用 Retrofit2 ?
我无法在网上找到任何关于如何继续的资源或教程;我发现
更新:
这是我的代码:
FileDownloadService.java
public interface FileDownloadService {
@GET(Constants.UPLOADS_DIRECTORY + "/{filename}")
@Streaming
Call<ResponseBody> downloadRetrofit(@Path("filename") String fileName);
}
MainActivity.java(@Blackbelt的解决方案)
private void downloadFile(String filename) {
FileDownloadService service = ServiceGenerator
.createService(FileDownloadService.class, Constants.SERVER_IP_ADDRESS);
Call<ResponseBody> call = service.downloadRetrofit("db90408a4bb1ee65d3e09d261494a49f.jpg");
call.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(final Response<ResponseBody> response, Retrofit retrofit) {
try {
InputStream is = response.body().byteStream();
FileOutputStream fos = new FileOutputStream(
new File(Environment.getExternalStorageDirectory(), "image.jpg")
);
int read = 0;
byte[] buffer = new byte[32768];
while ((read = is.read(buffer)) > 0) {
fos.write(buffer, 0, read);
}
fos.close();
is.close();
} catch (Exception e) {
Toast.makeText(MainActivity.this, "Exception: " + e.toString(), Toast.LENGTH_LONG).show();
}
}
@Override
public void onFailure(Throwable t) {
Toast.makeText(MainActivity.this, "Failed to download file...", Toast.LENGTH_LONG).show();
}
});
}
当 USB 调试处于活动状态时,我得到一个 FileNotFoundException,而不是 NetworkOnMainThreadException。
MainActivity.java:(@Emanuel的解决方案)
private void downloadFile(String filename) {
FileDownloadService service = ServiceGenerator
.createService(FileDownloadService.class, Constants.SERVER_IP_ADDRESS);
Call<ResponseBody> call = service.downloadRetrofit("db90408a4bb1ee65d3e09d261494a49f.jpg");
call.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(final Response<ResponseBody> response, Retrofit retrofit) {
Log.i(TAG, "external storage = " + (Environment.getExternalStorageState() == null));
Toast.makeText(MainActivity.this, "Downloading file... " + Environment.getExternalStorageDirectory(), Toast.LENGTH_LONG).show();
File file = new File(Environment.getDataDirectory().toString() + "/aouf/image.jpg");
try {
file.createNewFile();
Files.asByteSink(file).write(response.body().bytes());
} catch (Exception e) {
Toast.makeText(MainActivity.this,
"Exception: " + e.toString(),
Toast.LENGTH_LONG).show();
}
}
@Override
public void onFailure(Throwable t) {
Toast.makeText(MainActivity.this, "Failed to download file...", Toast.LENGTH_LONG).show();
}
});
}
我得到一个 FileNotFoundException。
要下载 文件,您可能需要响应的原始 InputStream
并写入 sdcard
上的内容。为此,对于 return 类型 Call<ResponseBody>
,您应该使用 ResponseBody 作为 T
。然后,您将使用 Retrofit
到 enqueue
一个
Callback<ResponseBody>
当 onResponse
@Override
public void onResponse(final Response<ResponseBody> response, Retrofit retrofit) {
被调用,你可以检索InputStream
,用response.byteStream()
,从中读取,并将你读取的内容写入sdcard(看看here)
这是一个展示如何下载 Retrofit JAR 文件的小例子。您可以根据自己的需要进行调整。
这是界面:
import com.squareup.okhttp.ResponseBody;
import retrofit.Call;
import retrofit.http.GET;
import retrofit.http.Path;
interface RetrofitDownload {
@GET("/maven2/com/squareup/retrofit/retrofit/2.0.0-beta2/{fileName}")
Call<ResponseBody> downloadRetrofit(@Path("fileName") String fileName);
}
这是一个Javaclass使用界面:
import com.google.common.io.Files;
import com.squareup.okhttp.ResponseBody;
import retrofit.Call;
import retrofit.Callback;
import retrofit.Response;
import retrofit.Retrofit;
import java.io.File;
import java.io.IOException;
public class Main {
public static void main(String... args) {
Retrofit retrofit = new Retrofit.Builder().
baseUrl("http://repo1.maven.org").
build();
RetrofitDownload retrofitDownload = retrofit.create(RetrofitDownload.class);
Call<ResponseBody> call = retrofitDownload.downloadRetrofit("retrofit-2.0.0-beta2.jar");
call.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(Response<ResponseBody> response, Retrofit retrofitParam) {
File file = new File("retrofit-2.0.0-beta2.jar");
try {
file.createNewFile();
Files.asByteSink(file).write(response.body().bytes());
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void onFailure(Throwable t) {
}
});
}
}
如果有人无意中发现了这个响应,这就是我将改造与 Rx 结合使用的方式。每个下载的文件都会被缓存,任何具有相同 url 的后续请求都将 return 已下载的文件。
为了使用它,只需订阅这个 observable 并传递你的 url。这会将您的文件保存在下载目录中,因此如果您的应用面向 API 23 或更高版本,请确保请求权限。
public Observable<File> getFile(final String filepath) {
URL url = null;
try {
url = new URL(filepath);
} catch (MalformedURLException e) {
e.printStackTrace();
}
final String name = url.getPath().substring(url.getPath().lastIndexOf("/") + 1);
final File file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), name);
if (file.exists()) {
return Observable.just(file);
} else {
return mRemoteService.getFile(filepath).flatMap(new Func1<Response<ResponseBody>, Observable<File>>() {
@Override
public Observable<File> call(final Response<ResponseBody> responseBodyResponse) {
return Observable.create(new Observable.OnSubscribe<File>() {
@Override
public void call(Subscriber<? super File> subscriber) {
try {
final File file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getAbsoluteFile(), name);
BufferedSink sink = Okio.buffer(Okio.sink(file));
sink.writeAll(responseBodyResponse.body().source());
sink.flush();
sink.close();
subscriber.onNext(file);
subscriber.onCompleted();
file.deleteOnExit();
} catch (IOException e) {
Timber.e("Save pdf failed with error %s", e.getMessage());
subscriber.onError(e);
}
}
});
}
});
}
}
改造部分调用
@Streaming
@GET
Observable<retrofit2.Response<ResponseBody>> getFile(@Url String fileUrl);