如何通过 HTTP POST 方法使用 Retrofit 或 Picasso 下载图像

How to download image using Retrofit or Picasso via HTTP POST method

我有一个 HTTP post 请求,它需要一个包含以下内容的 JSON 作为请求 body:

{
  "UserName":"ApiService", 
  "Password":"BandarAndroid",
  "AndroidId":"15df3b3a90XXXXX",
  "ContentId":"704",
  "frame":"1"
}

向服务器请求后,我只得到一张图像作为响应(而不是任何东西(如 JSON))。 适当的图像是根据要求创建的,没有网址。 我的服务地址是:

http://xyz.website.com/api/DownloadFileForAndroid

我的回复 header 是:

cache-control →no-cache
content-length →29837
content-type →image/png
date →Mon, 09 Sep 2019 08:42:23 GMT
expires →-1
pragma →no-cache
server →Microsoft-IIS/8.5
x-aspnet-version →4.0.30319
x-powered-by →ASP.NET

不知道是用retrofit还是Picasso来拍这张照片,

in Picasso: 我无法在请求 body 中发送 JSON 的金额。

在改造中:没有 url 我无法获取照片(地址指向像 www.site.com/a.jpg 的照片)

I don't know whether to use retrofit or Picasso to get this photo

最好使用Picasso,否则如果使用Retrofit下载图像,您必须编写大量代码才能有效地加载图像.

你会很高兴知道你可以根据你的选择使用 Retrofit 和 Picasso 从 API.

加载图像

在继续示例之前,我想澄清一件事,您误以为您需要将 above-mentioned JSON 数据发送为 header 但在玩弄 API 之后,我发现它需要 JSON 作为请求 body.


例子

RemoteApi.kt

interface RemoteApi {
    // Retrofit
    @Streaming  // Important
    @POST("/api/DownloadFileForAndroid")
    @Throws(Exception::class)
    suspend fun getImage(@Body body: ImageRequest): ResponseBody?

    companion object {
        // Retrofit
        fun create(): RemoteApi {
            val retrofit = Retrofit.Builder()
                    .addConverterFactory(GsonConverterFactory.create())
                    .baseUrl("http://shop.atiafkar.ir")
                    .build()
            return retrofit.create(RemoteApi::class.java)
        }
        // Picasso
        fun getPicassoImageRequest(
                builder : Picasso.Builder,
                body: String,
                url: String = "http://shop.atiafkar.ir/api/DownloadFileForAndroid"
        ) = builder.downloader(OkHttp3Downloader(getPicassoCallFactory(body)))
                        .build()
                        .load(url)
        // Picasso
        private fun getPicassoCallFactory(jsonBody : String): Call.Factory {
            return Call.Factory { request ->
                OkHttpClient().run {
                    RequestBody.create(MediaType.parse("application/json"), jsonBody).let {
                        newCall(request.newBuilder()
                                .post(it)
                                .addHeader("Content-Type", "application/json")
                                .build()
                        )
                    }
                }
            }
        }
    }
}

ImageRepository.kt

class ImageRepository(private val api: RemoteApi = RemoteApi.create()) {
    companion object {
        fun get() = ImageRepository()
    }
    // Retrofit
    suspend fun downloadImage(body : ImageRequest = ImageRequest.default): Bitmap? {
        return api.getImage(body)?.run {
            withContext(Dispatchers.IO) {
                bytes().let {
                    // to load bitmap efficiently follow the guideline provided by -
                    //  https://developer.android.com/topic/performance/graphics/load-bitmap
                    // otherwise you may experience OutOfMemoryException
                    BitmapFactory.decodeByteArray(it, 0, it.size)
                }
            }
        }
    }
    // Picasso
    fun getPicassoImageLoader(
            builder : Picasso.Builder,
            body: ImageRequest = ImageRequest.default
    ) = RemoteApi.getPicassoImageRequest(builder, body.toJson())
}

ImageViewModel.kt

class ImageViewModel(private val repository: ImageRepository) : ViewModel() {
    private val _progress = MutableLiveData<Boolean>()
    val progress = _progress
    // Retrofit
    val liveImage = liveData {
        _progress.value = true
        emit(repository.downloadImage())
        _progress.value = false
    }
    // Picasso
    fun getPicassoImageLoader(builder: Picasso.Builder) = repository.getPicassoImageLoader(builder)
}

最后,

ImageActivity.kt

class ImageActivity : AppCompatActivity() {
    private lateinit var dataBinding : ActivityImageBinding
    private val imageViewModel by lazy { ViewModelProviders.of(this, ImageViewModelFactory()).get(ImageViewModel::class.java) }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        dataBinding = DataBindingUtil.setContentView(this, R.layout.activity_image)
        dataBinding.lifecycleOwner = this
        dataBinding.viewModel = imageViewModel
        // Retrofit
        imageViewModel.liveImage.observe(this, Observer {
            dataBinding.imageView.setImageBitmap(it)
        })
        // Picasso
        imageViewModel.getPicassoImageLoader(Picasso.Builder(this)).into(dataBinding.imageView2)
    }
}

ImageRequest.kt

data class ImageRequest(
        @field:SerializedName("UserName")
        val userName: String? = null,
        @field:SerializedName("ContentId")
        val contentId: String? = null,
        @field:SerializedName("AndroidId")
        val androidId: String? = null,
        @field:SerializedName("Password")
        val password: String? = null,
        @field:SerializedName("frame")
        val frame: String? = null
) {
    companion object {
        val default = ImageRequest(
                "ApiService",
                "704",
                "15df3b3a90dc5688",
                "BandarAndroid",
                "1"
        )
    }
}

fun ImageRequest.toJson() : String {
    return Gson().toJson(this, ImageRequest::class.java)
}

As you requested, I am converting my previous solution (Kotlin) to Java

示例 1:毕加索

public void loadImageWithPicasso(ImageView imageView) {
    Picasso.Builder builder = new Picasso.Builder(imageView.getContext());
    RequestCreator picassoImageLoader = createPicassoLoader(
            builder,
            ImageRequest.DEFAULT_JSON_BODY,
            "http://shop.atiafkar.ir/api/DownloadFileForAndroid"
    );
    picassoImageLoader.into(imageView);
}

public RequestCreator createPicassoLoader(Picasso.Builder builder, String body, String url) {
    return builder.downloader(new OkHttp3Downloader(createPicassoCallFactory(body)))
            .build()
            .load(url);
}

private okhttp3.Call.Factory createPicassoCallFactory(String jsonBody) {
    final OkHttpClient okHttpClient = new OkHttpClient.Builder()
            .build();
    final RequestBody requestBody = RequestBody.create(MediaType.parse("application/json"), jsonBody);
    return new okhttp3.Call.Factory() {
        @Override
        public okhttp3.Call newCall(Request request) {
            Request.Builder builder = request.newBuilder();
            builder.post(requestBody);
            builder.addHeader("Content-Type", "application/json");
            return okHttpClient.newCall(builder.build());
        }
    };
}

示例 2:改造

public void loadImageWithRetrofit(ImageView imageView) {
        final RetrofitImageLoader imageLoader = new RetrofitImageLoader(imageView);
        RemoteApi api = RemoteApi.Factory.create();

        api.getImage(ImageRequest.DEFAULT_BODY).enqueue(new Callback<ResponseBody>() {
            @Override
            public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
                ResponseBody body = response.body();
                if (response.isSuccessful() && body != null) {
                    imageLoader.execute(body.byteStream());
                } else {
                    Log.d(TAG, "Retrofit onResponse(): CODE = [" + response.code() + "], MESSAGE = [" + response.message() + "]");
                }
            }

            @Override
            public void onFailure(Call<ResponseBody> call, Throwable t) {
                Log.d(TAG, "Retrofit onFailure(): t = [" + t + "]");
            }
        });
}

RetrofitImageLoader class

public class RetrofitImageLoader extends AsyncTask<InputStream, Integer, Bitmap> {
        private ImageView imageView;

        private RetrofitImageLoader(ImageView imageView) {
            this.imageView = imageView;
        }

        @Override
        protected Bitmap doInBackground(InputStream... inputStreams) {
            return BitmapFactory.decodeStream(inputStreams[0]);
        }

        @Override
        protected void onPostExecute(Bitmap bitmap) {
            super.onPostExecute(bitmap);
            imageView.setImageBitmap(bitmap);
        }
}

RemoteApi interface

public interface RemoteApi {
    @Streaming  // Important
    @POST("/api/DownloadFileForAndroid")
    Call<ResponseBody> getImage(@Body ImageRequest body);

    class Factory {
        private static RemoteApi mInstance;

        public static RemoteApi create() {
            if (mInstance == null) {
                mInstance = new Retrofit.Builder()
                        .addConverterFactory(GsonConverterFactory.create())
                        .baseUrl("http://shop.atiafkar.ir")
                        .build()
                        .create(RemoteApi.class);
            }
            return mInstance;
        }
    }
}

ImageRequest 模型 class

public class ImageRequest{
    public static final ImageRequest DEFAULT_BODY;
    public static final String DEFAULT_JSON_BODY;

    static {
        DEFAULT_BODY = new ImageRequest();
        DEFAULT_BODY.setAndroidId("15df3b3a90dc5688");
        DEFAULT_BODY.setContentId("704");
        DEFAULT_BODY.setFrame("1");
        DEFAULT_BODY.setPassword("BandarAndroid");
        DEFAULT_BODY.setUserName("ApiService");

        DEFAULT_JSON_BODY = new Gson().toJson(DEFAULT_BODY, ImageRequest.class);
    }

    @SerializedName("UserName")
    private String userName;
    @SerializedName("ContentId")
    private String contentId;
    @SerializedName("AndroidId")
    private String androidId;
    @SerializedName("Password")
    private String password;
    @SerializedName("frame")
    private String frame;

    public void setUserName(String userName){
        this.userName = userName;
    }
    public void setContentId(String contentId){
        this.contentId = contentId;
    }
    public void setAndroidId(String androidId){
        this.androidId = androidId;
    }
    public void setPassword(String password){
        this.password = password;
    }
    public void setFrame(String frame){
        this.frame = frame;
    }
}