Flutter Image.network()加载图片报403错误

Flutter 报错403原因

======== Exception caught by image resource service ================================================
The following NetworkImageLoadException was thrown resolving an image codec:
HTTP request failed, statusCode: 403, https://p2.music.126.net/GvYQoflE99eoeGi9jG4Bsw==/109951165375336156.jpg

When the exception was thrown, this was the stack: 
#0      NetworkImage._loadAsync (package:flutter/src/painting/_network_image_io.dart:150:9)
<asynchronous suspension>
Image provider: NetworkImage("https://p2.music.126.net/GvYQoflE99eoeGi9jG4Bsw==/109951165375336156.jpg", scale: 1.0)
Image key: NetworkImage("https://p2.music.126.net/GvYQoflE99eoeGi9jG4Bsw==/109951165375336156.jpg", scale: 1.0)
====================================================================================================

 Flutter加载图片爆403错误,然而通过浏览器访问此url却能正常访问,原因是由于服务器设置了防伪链的措施,导致使用Image.network()加载图片的时候服务器拒绝客户端的请求


解决思路

Referer


Referer是 HTTP 请求头中的一个字段,它记录了当前请求页面来自的页面地址。通过这个字段,我们可以追踪访客的来源,了解他们是从哪个页面链接访问当前页面的。

防盗链


当用户浏览网页时,如果他们点击链接或图片跳转到新页面,那么前一个页面的URL就会作为Referer字段自动包含在HTTP请求头中。对于图片请求,Referer通常指的是图片所在网页的URL。浏览器会自动将Referer字段添加到发出的请求中,这样服务器就可以了解用户是如何到达当前页面的。

比如说,我把图片文件放到Gitee上,然后我写了一个客户端每次都去调用他,这样子相当于借用了Gitee的服务器资源,这些服务资源都是需要付费的,而我从中获得了利益。这种行为就相当于盗链。

通常服务器防盗链技术一般有以下几种方式

  • 通过请求头的Referer来判定
  • 通过检验Cookie、Session来进行判定
  • 通过定期修改文件名以及文件链接

思路


既然我们已经知道服务器是根据客户端请求的Referer内容来判定是否有权限访问此图片资源,我们可以把请求头的Referer去掉,以模拟初始使用浏览器访问该图片资源的情况,避免被服务器检索出盗链。

解决方法


通过http请求自定义修改请求头,首先导入dio依赖

dependencies:
  flutter:
    sdk: flutter
  #网络请求包
  dio: ^5.3.3

在lib目录下创建一个自定义请求类封装dio,使其get请求接口暴露给使用者,来达到我们想要的自定义模拟请求头

import 'dart:typed_data';
import 'package:dio/dio.dart';

const String bytes_user_agent =
    "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36 Edg/118.0.2088.76";

class SSJRequestManager {
  final Dio _dio = Dio();

  Dio getDio() {
    return _dio;
  }

  SSJRequestManager();

  // 获取二进制数据
  Future<Uint8List> get_bytes(String path, Map<String, dynamic> params) async {
    var options = Options(
        method: "GET",
        responseType: ResponseType.bytes,
        headers: {"User-Agent": bytes_user_agent});

    return await _dio
        .get(path, queryParameters: params, options: options)
        .then((value) => value.data);
  }
}

final SSJRequestManager ssjRequestManager = SSJRequestManager();

然后在需要调用的地方使用我们定义好的get_bytes函数来获取二进制字节数据,用于构建图片,这里要注意的是  responseType:ResponseType.bytes  这一句,设置返回响应体为我们想要的   Uint8List  数据类型然后我们在需要构建组件的地方,使用  FutureBuilder  来异步构建组件模块

import 'package:flutter/material.dart';
import 'package:yqplaymusic/common/utils/request.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: Scaffold(
        body: Center(
          child: SizedBox(
            height: 100,
            width: 100,
            child: FutureBuilder(
              future: ssjRequestManager.get_bytes(
                  "http://p2.music.126.net/GvYQoflE99eoeGi9jG4Bsw==/109951165375336156.jpg",
                  {}),
              builder: (BuildContext context, AsyncSnapshot<dynamic> snapshot) {
                if(snapshot.connectionState == ConnectionState.done)
                  {
                    return Image.memory(snapshot.data);
                  }
                else
                  {
                     return const Icon(Icons.file_download);
                    //return Image.network("https://p2.music.126.net/GvYQoflE99eoeGi9jG4Bsw==/109951165375336156.jpg");
                  }
              },
            ),
          ),
        ),
      ),
    );
  }
}

猜你喜欢

转载自blog.csdn.net/qianxiamuxin/article/details/134190195