【问题标题】:How to download image using Retrofit or Picasso via HTTP POST method如何通过 HTTP POST 方法使用 Retrofit 或 Picasso 下载图像
【发布时间】:2019-09-09 08:05:08
【问题描述】:

我有一个 HTTP post 请求,它需要一个 JSON 作为请求体:

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

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

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

我的响应头是:

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

我不知道是用改造还是毕加索来得到这张照片,

在 Picasso 中:我无法在请求正文中发送 JSON 的数量。

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

【问题讨论】:

  • 如果您添加OkHttpDownloader stackoverflow.com/questions/49673110/…,我认为您可以使用Picasso 完成此操作
  • 除非我不明白您的问题,但是您期望的响应类型是什么?你的content-type回复是image/png,是正常的。
  • 不知道是用改造还是毕加索来拍这张照片,@HarryCoder
  • 毕加索不明白我想要这个帖子,它给出了一个错误 405 method not allowed get 。 @coroutineDispatcher
  • 你能提供一个有效的url和有效的请求参数来为你检查并找到解决方案吗?

标签: java android retrofit2 picasso


【解决方案1】:

根据您的要求,我将以前的解决方案 (Kotlin) 转换为 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;
    }
}

【讨论】:

  • 这是我的荣幸 :)
  • 如何解决此错误:使用 JsonReader.setLenient(true) 在第 1 行第 1 列路径接受格式错误的 JSON $ 在调用改造模式 @Roaim 时出现此错误
  • 必须使用 okhttp3 或 squareup.okhttp 吗?
  • @sajjadYosefi 关注stackoverflow.com/a/36002007/5465447,您将能够解决格式错误的 JSON 异常。
  • @sajjadYosefi 我认为您也可以使用 square.okhttp。不过不确定。你可以试一试。如果没有构建错误,您就可以开始了。
【解决方案2】:

我不知道是用改造还是毕加索来得到这张照片

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

您会很高兴知道您可以同时使用 Retrofit 和 Picasso,具体取决于您从该 API 加载图像的选择。


在继续举例之前,我想澄清一件事,即您有一个误解,即您需要将上述 JSON 数据作为标头发送,但在使用 API 之后,我发现它需要JSON 作为请求正文。


示例

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)
}

【讨论】:

  • @Farid 感谢您的建议。我很抱歉写了一个很长的答案。我会尽量使其简洁,并在未来记住这一点。但是当您说我的回答总体上无关紧要时,我感到很失望。基本上,我认为如果我给他完整的代码以便他可以复制和运行,同时将代码分成几部分以便易于理解,这将对他有所帮助。但我觉得我错了。
  • 是的,我可以。您能否列出您面临的难以理解的希望我转换为 java 的功能?还是你想让我转换所有东西?
  • 我对Kotlin一无所知,我不懂代码@Roaim
  • @sajjadYosefi 我已经提交了另一个 Java 解决方案。请看一下,如果它解决了您的问题,请告诉我?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-09-02
  • 2019-12-28
  • 2018-06-02
  • 1970-01-01
  • 2017-06-07
相关资源
最近更新 更多