You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter集成1Fichier API上传卡滞:验证请求无法推进求助

Flutter 1Fichier上传验证请求卡住问题

程序执行到“发送验证请求...”步骤时卡住,无法继续推进。怀疑verifyUpload方法存在错误,或文件未实际上传成功(1Fichier平台无新增文件)。以下是完整代码:

主程序入口

import 'dart:convert';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:file_picker/file_picker.dart';
import 'package:path/path.dart'; // 用于获取文件名(basename)

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: '1Fichier Uploader',
      theme: ThemeData(primarySwatch: Colors.blue),
      home: HomePage(),
    );
  }
}

上传工具类

class FichierUploader {
  Future<Map<String, String>?> getLinkAndId() async {
    debugPrint('调用getLink');
    var uri = Uri.parse("https://api.1fichier.com/v1/upload/get_upload_server.cgi");

    try {
      var response = await http.get(
        uri,
        headers: {
          'Accept': 'application/json',
          'Content-Type': 'application/json',
        },
      );

      if (response.statusCode == 200) {
        Map<String, dynamic> data = jsonDecode(response.body);

        if (data.containsKey('url') && data.containsKey('id')) {
          String url = data['url'];
          String id = data['id'];
          debugPrint('URL: $url');
          debugPrint('ID: $id');
          return {'url': url, 'id': id};
        } else {
          debugPrint('JSON响应不完整 : ${response.body}');
        }
      } else {
        debugPrint('错误: ${response.statusCode} - ${response.body}');
      }
    } catch (e) {
      debugPrint('请求时出错: $e');
    }

    return null;
  }

  Future<void> upload(File file) async {
    Map<String, String>? linkData = await getLinkAndId();
    String apiKey = "EWAi0q8s8z2htQjJe90T7j=6OqBKncgo";

    if (linkData != null) {
      String url = linkData['url']!;
      String id = linkData['id']!;
      debugPrint('🚀 开始上传,URL: $url ,ID: $id');

      var uri = Uri.parse("https://$url/upload.cgi?id=$id");
      debugPrint('🌍 上传URL: $uri');

      try {
        http.MultipartRequest request = http.MultipartRequest('POST', uri);

        // 添加Authorization
        request.headers['Authorization'] = 'Bearer $apiKey';

        // 添加文件,字段为"file[]"并指定文件名
        if (file.existsSync()) {
          request.files.add(
            await http.MultipartFile.fromPath(
              'file[]',
              file.path,
              filename: basename(file.path),
            ),
          );
        } else {
          debugPrint('⚠️ 指定位置的文件不存在。');
          return;
        }

        // 发送请求
        var response = await request.send();

        if (response.statusCode == 200) {
          debugPrint('✅ 上传成功(200) : 程序已正常完成。');
          // 等待几秒让服务器处理上传
          await Future.delayed(const Duration(seconds: 5));
          await verifUpload(url, id);
        } else if (response.statusCode == 302) {
          // 处理重定向
          String? redirectionUrl = response.headers['location'];
          if (redirectionUrl != null) {
            String fullRedirectionUrl = "https://$url$redirectionUrl";
            debugPrint('🔄 重定向(302) : 点击链接查看报告 -> $fullRedirectionUrl');
            await Future.delayed(const Duration(seconds: 5));
            await verifUpload(url, id);
          } else {
            debugPrint('⚠️ 检测到重定向(302)但未提供URL。');
          }
        } else if (response.statusCode == 500) {
          debugPrint('❌ 服务器内部错误(500) : 无法恢复的错误。');
        } else {
          debugPrint('⚠️ 意外错误(${response.statusCode}) : ${await response.stream.bytesToString()}');
        }
      } catch (e) {
        debugPrint('🛑 上传时出错 : $e');
      }
    } else {
      debugPrint('❌ 无法获取上传信息。');
    }
  }

  Future<void> verifUpload(String url, String id) async {
    debugPrint('🚀 开始验证,URL: $url ,ID: $id');

    // 使用JSON=2用于调试
    var uri = Uri.parse("https://$url/end.pl?xid=$id&JSON=2");
    debugPrint('🌍 验证URL: $uri');

    try {
      debugPrint('发送验证请求...');
      // 超时时间增加到60秒
      var response = await http.get(uri).timeout(const Duration(seconds: 60));
      debugPrint('请求已发送。状态码: ${response.statusCode}');
      debugPrint('响应内容: ${response.body}');

      if (response.statusCode == 200) {
        debugPrint('✅ 验证成功,已收到JSON响应。');
        Map<String, dynamic> jsonResponse = jsonDecode(response.body);

        if (jsonResponse.containsKey('links') && jsonResponse['links'].isNotEmpty) {
          var linkData = jsonResponse['links'][0];
          String downloadLink = linkData['download'];
          String filename = linkData['filename'];
          String removeLink = linkData['remove'];
          String size = linkData['size'];

          debugPrint('🔗 下载链接: $downloadLink');
          debugPrint('📄 文件名: $filename');
          debugPrint('🗑 删除链接: $removeLink');
          debugPrint('📦 文件大小: $size');
        } else {
          debugPrint('⚠️ 响应JSON中未找到链接。');
        }
      } else {
        debugPrint('⚠️ 意外响应(${response.statusCode}) : ${response.body}');
      }
    } catch (e) {
      debugPrint('🛑 验证时出错 : $e');
    }
  }
}

主页Widget

class HomePage extends StatefulWidget {
  const HomePage({super.key});
  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  File? selectedFile;

  Future<void> selectFile() async {
    FilePickerResult? result = await FilePicker.platform.pickFiles();

    if (result != null) {
      setState(() {
        selectedFile = File(result.files.single.path!);
      });
      debugPrint('已选择文件: ${selectedFile!.path}');
    } else {
      debugPrint('未选择任何文件。');
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('上传到1Fichier')),
      body: Padding(
        padding: const EdgeInsets.all(20.0),
        child: Column(
          children: [
            ElevatedButton(
              onPressed: selectFile,
              child: const Text('选择文件'),
            ),
            const SizedBox(height: 20),
            selectedFile == null
                ? const Text('未选择任何文件')
                : Text('已选择文件: ${selectedFile!.path}'),
            const SizedBox(height: 20),
            ElevatedButton(
              onPressed: selectedFile != null
                  ? () => FichierUploader().upload(selectedFile!)
                  : null,
              child: const Text('上传'),
            ),
          ],
        ),
      ),
    );
  }
}

问题排查与修复方案

1. 上传请求缺少必要参数

根据1Fichier API规范,上传请求除了Authorization和file[],还需携带id参数作为表单字段。修改upload方法,在添加文件后新增:

request.fields['id'] = id;

2. 验证请求参数错误

当前验证请求使用了错误的参数名xid,正确参数应为id。修改verifUpload方法中的URL:

var uri = Uri.parse("https://$url/end.pl?id=$id&JSON=2");

3. 优化上传后等待逻辑

无需固定等待5秒,可缩短等待时间至2秒,避免不必要的阻塞:

await Future.delayed(const Duration(seconds: 2));

4. 明确超时异常处理

为验证请求添加更清晰的超时提示,区分超时与其他异常:

try {
  var response = await http.get(uri).timeout(
    const Duration(seconds: 60),
    onTimeout: () {
      debugPrint('🛑 验证请求超时');
      return http.Response('Timeout', 408);
    },
  );
  // 后续响应处理逻辑
} on TimeoutException catch (_) {
  debugPrint('🛑 验证请求超时');
} catch (e) {
  debugPrint('🛑 验证时出错 : $e');
}

5. 检查API密钥权限

确认你的API密钥拥有上传权限,可通过官方API文档的测试接口验证密钥有效性。

内容的提问来源于stack exchange,提问作者Gabriel

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 13:14:54