Image.network() in Flutter returns 403 solution when requesting an image link

Image.network() in Flutter returns 403 solution when requesting an image link

Problem recurrence

I have a link forhttps://p2.music.126.net/5CJeYN35LnzRDsv5Lcs0-Q==/109951165374966765.jpgPicture, now I pass it by callingImage.network("https://p2.music.126.net/5CJeYN35LnzRDsv5Lcs0-Q==/109951165374966765.jpg") With this method, the console reports an error output of 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/5CJeYN35LnzRDsv5Lcs0-Q==/109951165374966765.jpg

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

Problem cause analysis

It is normal for browsers to request discovery

image-2.png

Postman can also obtain image binary data normally

image-3.png

When I tested, I could also obtain normal binary data by adding request headers to the third-party dio library.

// 获取二进制数据
Future<ResultData> getBytes(
  String path, {
    
    
  Map<String, dynamic>? queryParameters,
  MyOptions? myOptions,
}) async {
    
    
  myOptions ??= MyOptions(); // 为空则创建
  myOptions.method = "GET";
  myOptions.responseType = ResponseType.bytes; // 设置返回类型为二进制格式
  myOptions.headers = {
    
    "User-Agent": bytesUserAgent};
  Response response = await _dio.get(path,
      queryParameters: queryParameters, options: myOptions);
  return response.data as ResultData;
}

But after adding the request header inImage.network(), I still found an error, so I initially suspected Flutter's Image.network() network request Something went wrong


We checkImage.network()source code


  Image.network(
    String src, {
    
    
    super.key,
    double scale = 1.0,
    this.frameBuilder,
    this.loadingBuilder,
    this.errorBuilder,
    this.semanticLabel,
    this.excludeFromSemantics = false,
    this.width,
    this.height,
    this.color,
    this.opacity,
    this.colorBlendMode,
    this.fit,
    this.alignment = Alignment.center,
    this.repeat = ImageRepeat.noRepeat,
    this.centerSlice,
    this.matchTextDirection = false,
    this.gaplessPlayback = false,
    this.filterQuality = FilterQuality.low,
    this.isAntiAlias = false,
    Map<String, String>? headers,
    int? cacheWidth,
    int? cacheHeight,
  }) : image = ResizeImage.resizeIfNeeded(cacheWidth, cacheHeight, NetworkImage(src, scale: scale, headers: headers)),
       assert(cacheWidth == null || cacheWidth > 0),
       assert(cacheHeight == null || cacheHeight > 0);

It can be seen thatImage.network() created an objectNetworkImage. Let’s follow up and view the source code


abstract class NetworkImage extends ImageProvider<NetworkImage> {
    
    
  /// Creates an object that fetches the image at the given URL.
  ///
  /// The arguments [url] and [scale] must not be null.
  const factory NetworkImage(String url, {
    
     double scale, Map<String, String>? headers }) = network_image.NetworkImage;

  /// The URL from which the image will be fetched.
  String get url;

  /// The scale to place in the [ImageInfo] object of the image.
  double get scale;

  /// The HTTP headers that will be used with [HttpClient.get] to fetch image from network.
  ///
  /// When running flutter on the web, headers are not used.
  Map<String, String>? get headers;

  
  ImageStreamCompleter load(NetworkImage key, DecoderCallback decode);

  
  ImageStreamCompleter loadBuffer(NetworkImage key, DecoderBufferCallback decode);

  
  ImageStreamCompleter loadImage(NetworkImage key, ImageDecoderCallback decode);
}

We can find that this code defines a factory function named NetworkImage, which accepts a string type url and an optional scale parameter (double precision floating point type), and an optional headers parameter (string map), so this is an abstract class, so we also To continue to follow up, click network_image.NetworkImageNetworkImage

By reading the source code, we can find


Future<ui.Codec> _loadAsync(
  NetworkImage key,
  StreamController<ImageChunkEvent> chunkEvents, {
    
    
  image_provider.ImageDecoderCallback? decode,
  image_provider.DecoderBufferCallback? decodeBufferDeprecated,
  image_provider.DecoderCallback? decodeDeprecated,
}) async {
    
    
  try {
    
    
    assert(key == this);
    final Uri resolved = Uri.base.resolve(key.url);
    final HttpClientRequest request = await _httpClient.getUrl(resolved);
    headers?.forEach((String name, String value) {
    
    
      request.headers.add(name, value);
    });
    final HttpClientResponse response = await request.close();
    if (response.statusCode != HttpStatus.ok) {
    
    
      // The network may be only temporarily unavailable, or the file will be
      // added on the server later. Avoid having future calls to resolve
      // fail to check the network again.
      await response.drain<List<int>>(<int>[]);
      throw image_provider.NetworkImageLoadException(
          statusCode: response.statusCode, uri: resolved);
    }
    final Uint8List bytes = await consolidateHttpClientResponseBytes(
      response,
      onBytesReceived: (int cumulative, int? total) {
    
    
        chunkEvents.add(ImageChunkEvent(
          cumulativeBytesLoaded: cumulative,
          expectedTotalBytes: total,
        ));
      },
    );
    if (bytes.lengthInBytes == 0) {
    
    
      throw Exception('NetworkImage is an empty file: $resolved');
    }
    if (decode != null) {
    
    
      final ui.ImmutableBuffer buffer =
          await ui.ImmutableBuffer.fromUint8List(bytes);
      return decode(buffer);
    } else if (decodeBufferDeprecated != null) {
    
    
      final ui.ImmutableBuffer buffer =
          await ui.ImmutableBuffer.fromUint8List(bytes);
      return decodeBufferDeprecated(buffer);
    } else {
    
    
      assert(decodeDeprecated != null);
      return decodeDeprecated!(bytes);
    }
  } catch (e) {
    
    
    // Depending on where the exception was thrown, the image cache may not
    // have had a chance to track the key in the cache at all.
    // Schedule a microtask to give the cache a chance to add the key.
    scheduleMicrotask(() {
    
    
      PaintingBinding.instance.imageCache.evict(key);
    });
    rethrow;
  } finally {
    
    
    chunkEvents.close();
  }
}

Future<ui.Codec> _loadAsync()This function is whereImage.network() requests network data. According to the previous analysis, this error is most likely caused by a problem with one of the requested parameters


by reading

if (response.statusCode != HttpStatus.ok) {
    
    
  // The network may be only temporarily unavailable, or the file will be
  // added on the server later. Avoid having future calls to resolve
  // fail to check the network again.
  await response.drain<List<int>>(<int>[]);
  throw image_provider.NetworkImageLoadException(
      statusCode: response.statusCode, uri: resolved);
}

We can know that the 403 error reported in the control bar is because an exception was thrown here. We can conclude thatfinal HttpClientResponse response = await request.close(); this code is to initiate a network request. Let’s try to print it in front. Request header

output

I/flutter (31164): user-agent: Dart/3.1 (dart:io)
I/flutter (31164): accept-encoding: gzip
I/flutter (31164): content-length: 0
I/flutter (31164): host: p2.music.126.net

This is the same as what we analyzed.user-agentThere is an extra oneDart/3.1 (dart:io) in it, which is the cause of the problem

solution

We try to remove this user ID

image.png


Add the following code

// 去除user-agent中的 dart/3.1
request.headers.remove("user-agent", "Dart/3.1 (dart:io)");

At this time, run it again and find that the picture can be displayed normally.


image-1.png

Guess you like

Origin blog.csdn.net/qianxiamuxin/article/details/134268190