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");
                  }
              },
            ),
          ),
        ),
      ),
    );
  }
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
您可以使用`flutter`中的`paint`来将图片渲染为圆形图片。以下是示例代码: ```dart class CircleImage extends StatelessWidget { final String imageUrl; final double size; const CircleImage({Key key, this.imageUrl, this.size}) : super(key: key); @override Widget build(BuildContext context) { return CustomPaint( size: Size(size, size), painter: CircleImagePainter(imageUrl), ); } } class CircleImagePainter extends CustomPainter { final String imageUrl; CircleImagePainter(this.imageUrl); @override void paint(Canvas canvas, Size size) async { final image = await _loadImage(imageUrl); final paint = Paint() ..shader = ImageShader( image, TileMode.clamp, TileMode.clamp, Matrix4.identity().storage, ); final center = Offset(size.width / 2, size.height / 2); final radius = size.width / 2; canvas.drawCircle(center, radius, paint); } @override bool shouldRepaint(CircleImagePainter oldPainter) => oldPainter.imageUrl != imageUrl; Future<ui.Image> _loadImage(String imageUrl) async { final image = NetworkImage(imageUrl); final completer = Completer<ui.Image>(); image.resolve(ImageConfiguration()).addListener( ImageStreamListener( (imageInfo, _) => completer.complete(imageInfo.image), ), ); return await completer.future; } } ``` 在上述代码中,我们自定义了一个`CircleImage`组件,它接受一个`imageUrl`和`size`参数。在组件中,我们使用了`CustomPaint`来绘制圆形图片。我们创建了一个自定义的`CircleImagePainter`来实现`CustomPainter`,在`paint`方法中,我们首先通过`_loadImage`方法加载图片,然后使用`ImageShader`将图片渲染到圆形区域内,最后使用`drawCircle`方法绘制圆形。 需要注意的是,为了避免重复加载图片,我们在`shouldRepaint`方法中对`imageUrl`进行了比较,如果新的`imageUrl`与旧的不同,才会重新加载图片。 希望以上信息能够对您有所帮助。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值