代码之家  ›  专栏  ›  技术社区  ›  anthonymonori

在通过毕加索下载图像之前,如何通过内容长度和内容类型验证响应标题?

  •  0
  • anthonymonori  · 技术社区  · 8 年前

    我现在的情况是,在决定是否通过毕加索将图像下载到ImageView之前,我想验证HTTP响应头。具体原因是,如果不下载图像,我没有其他方法来判断图像的格式和大小是否正确,只是为了认识到我必须将其扔掉,因为它不符合标准集。

    流程大致如下:

    // 1. Ask a specific Profiles Service for a given user-id
    // 2. Process the response and get the avatar URI from it
    // 3. Call the avatar URI to validate the headers
    if (validate(response.headers)) {
        // 4. Download avatar image using Picasso into given ImageView
    } else {
        // 5. Don't download anything but show a placeholder image.
    }
    

    首先,我知道毕加索已经内置了显示占位符图像的功能,但URI并没有返回任何图像——它确实返回了,但对我们来说它不是一个正确的图像。因此,该功能对我们没有帮助。

    目前为止 .

    有什么想法可以绕过使用毕加索或URI的任何其他库来验证标头,从而避免无谓下载图像和浪费带宽吗?

    1 回复  |  直到 8 年前
        1
  •  0
  •   anthonymonori    8 年前

    如果您想跳到答案,请转到下面列表中的第3点!


    经过一段时间的摸索,我尝试了几种方法,找到了一种可以实现上面列出的所有功能的方法,尽管这不是最干净的方法:

    1.尝试在Picasso中添加OkHttp3拦截器,以修改传递之前的响应

    我的第一个想法是在OkHttp中使用拦截器,我们通常使用它来修改/添加头 请求 发送之前,不要修改 响应 -如果可能的话,你不应该这样做。

    拦截器如下所示:

    private static final Interceptor REWRITE_CACHE_CONTROL_INTERCEPTOR = new Interceptor() {
        @Override public okhttp3.Response intercept(Chain chain) throws IOException {
            okhttp3.Response originalResponse = chain.proceed(chain.request());
    
            if (validate(originalResponse.headers()) {
                return originalResponse.newBuilder()
                       .body(null) //remove the body, stupid
                       .build();
            }
            return originalResponse;
        }
    };
    

    然后,我尝试使用Jake Wharton的拦截器将此拦截器添加到毕加索实例中 com.jakewharton.picasso:picasso2-okhttp3-downloader 该库为毕加索2.5.2和Retrofit 2.0.2添加了OkHttp3拦截器支持。

    2.尝试制作 HEAD 在使用毕加索通话之前,请求使用Retrofit 2验证标题

    1.答 头部 请求在步骤2之前分别验证响应头。 2.答 GET 请求下载包含图像的整个响应主体(如果步骤1.返回有效)

    在我之前的方法中,我想将这两个步骤捆绑在一起,这在概念上是错误的,因为无论哪种方式,正文都会被下载,但只有在满足标准时才会被删除,因此无论哪种方法都会浪费带宽。

    我计划使用Retrofit 2实现步骤1,使用Picasso 2.5实现步骤2。

    由于为这样一个简单的调用实现Retrofit rest接口会带来很多开销,所以我很快从这个角度出发。

    3.通过做一个 头部 使用OkHttp3调用以验证标头,然后使用URI调用Picasso

    与方法#2类似,但不是使用Retrofit head()

    很快,我实现了一个AsyncTask,它将运行一个简单的OkHttp 头部() 调用一个给定的链接,它会返回一个包含化身和相关链接的枚举类型的项。此调用将在不同的线程上进行。

    初始化另一个调用的线程,一旦收到OkHttp调用的反馈(使用简单的模式通知UI线程),就会处理信息,并在需要时启动Picasso调用。

    package com.example;
    
    import android.net.Uri;
    
    public class Avatar {
        AvatarType _avatarType;
        Uri _uri;
    
        public Avatar(AvatarType type, Uri uri) {
            _avatarType = type;
            _uri = uri;
        }
    
        public AvatarType getType() {
            return _avatarType;
        }
    
        public Uri getUri() {
            return _uri;
        }
    }
    

    package com.example;
    
    public enum AvatarType {
        TYPE_X,
        TYPE_Y
    }
    

    package com.example;
    
    import android.net.Uri;
    import android.os.AsyncTask;
    
    import java.io.IOException;
    
    import okhttp3.OkHttpClient;
    import okhttp3.Request;
    
    public class AvatarDownloaderTask extends AsyncTask<String, String, Avatar> {
    
        private OnTaskCompleted _listener;
    
        public AvatarDownloaderTask(OnTaskCompleted listener){
            _listener = listener;
        }
    
    
        @Override
        protected Avatar doInBackground(String... params) {
            OkHttpClient client = new OkHttpClient();
    
            Request request = new Request.Builder()
                    .url(params[0])
                    .head()
                    .build();
    
            try {
                okhttp3.Response response = client.newCall(request).execute();
    
                if (Integer.parseInt(response.header("Content-Length")) > 100) {
                    return new Avatar(AvatarType.TYPE_Y, Uri.parse(params[0]));
                }
            } catch (IOException e) {
                cancel(true);
            }
            return new Avatar(AvatarType.TYPE_X, Uri.parse(params[0]));
        }
    
        @Override
        protected void onPostExecute(Avatar s) {
            super.onPostExecute(s);
            _listener.onTaskCompleted(s);
        }
    
        @Override
        protected void onCancelled(Avatar s) {
            super.onCancelled(s);
            _listener.onTaskCompleted(s);
        }
    
        public interface OnTaskCompleted{
            void onTaskCompleted(Avatar avatar);
        }
    }
    

    public class ProfileFragment extends Fragment implements AvatarDownloaderTask.OnTaskCompleted{
    
        /* -stripped out code- */
    
        // Use this call in your activity or fragment that also implements the OnTaskCompleted interface
        private void loadAvatar(String avatarUrl) {
            new AvatarDownloaderTask(this).execute(avatarUrl);
        }
    
        @Override
        public void onTaskCompleted(Avatar avatar) {
            switch (avatar.getType()) {
                case TYPE_X:
                    _avatarProgressBar.setVisibility(View.GONE);
                    _avatarImageView.setVisibility(View.GONE);
                    _avatarImageViewPlaceholder.setVisibility(View.VISIBLE);
                    break;
                case TYPE_Y:
                    // Just a helper class with a shared Picasso instance
                    ImageHandler.sharedHandler(getContext()).bypassImageResizer(avatar.getUri(), _avatarImageView, new com.squareup.picasso.Callback() {
                        @Override
                        public void onSuccess() {
                            _avatarProgressBar.setVisibility(View.GONE);
                            _avatarImageViewPlaceholder.setVisibility(View.GONE);
                            _avatarImageView.setVisibility(View.VISIBLE);
                        }
    
                        @Override
                        public void onError() {
                            _avatarProgressBar.setVisibility(View.GONE);
                            _avatarImageView.setVisibility(View.GONE);
                            _avatarImageViewPlaceholder.setVisibility(View.VISIBLE);
                        }
                    });
                    break;
            }
        }
    }
    

    这绝对不是一种干净的方法,迫使我这样做的API设计从一开始就不好。我很想听听其他人的反馈和建议,如何以另一种方式对此进行改进/修复。AsyncTask还有其他轻量级方法吗?如何将数据传回主/UI线程-这可以修复/改进/消除吗?

    干杯