← Назад к вопросам

Как отправить файл в теле POST-запроса?

1.0 Junior🔥 191 комментариев
#Работа с сетью

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI26 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Как отправить файл в теле POST-запроса?

Загрузка файлов — одна из самых частых операций в мобильных приложениях. Flutter предоставляет несколько способов для отправки файлов на сервер.

1. Использование http пакета с File

Простой способ отправки файла:

import 'package:http/http.dart' as http;
import 'dart:io';

Future<void> uploadFile(File file) async {
  var request = http.MultipartRequest(
    'POST',
    Uri.parse('https://api.example.com/upload'),
  );
  
  // Добавляем файл
  request.files.add(
    await http.MultipartFile.fromPath(
      'file',  // Имя поля в форме
      file.path,
    ),
  );
  
  // Добавляем дополнительные параметры если нужно
  request.fields['description'] = 'My uploaded file';
  request.fields['userId'] = '123';
  
  try {
    var response = await request.send();
    
    if (response.statusCode == 200) {
      print('Файл успешно загружен');
    } else {
      print('Ошибка: ${response.statusCode}');
    }
  } catch (e) {
    print('Ошибка загрузки: $e');
  }
}

2. Отправка файла как бинарные данные

Для отправки файла как содержимого POST-запроса:

import 'package:http/http.dart' as http;
import 'dart:io';

Future<void> uploadFileAsBinary(File file) async {
  try {
    // Читаем содержимое файла
    List<int> fileBytes = await file.readAsBytes();
    
    var response = await http.post(
      Uri.parse('https://api.example.com/upload'),
      headers: {
        'Content-Type': 'application/octet-stream',
        'Authorization': 'Bearer token',
      },
      body: fileBytes,
    );
    
    if (response.statusCode == 200) {
      print('Файл загружен успешно');
    } else {
      print('Ошибка: ${response.statusCode}');
    }
  } catch (e) {
    print('Ошибка: $e');
  }
}

3. Загрузка с отслеживанием прогресса

Используем dio пакет для отслеживания прогресса:

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

Future<void> uploadFileWithProgress(File file) async {
  final dio = Dio();
  
  try {
    FormData formData = FormData.fromMap({
      'file': await MultipartFile.fromFile(
        file.path,
        filename: file.path.split('/').last,
      ),
      'userId': '123',
    });
    
    Response response = await dio.post(
      'https://api.example.com/upload',
      data: formData,
      onSendProgress: (int sent, int total) {
        double progress = sent / total * 100;
        print('Загрузка: $progress%');
      },
    );
    
    if (response.statusCode == 200) {
      print('Успешно');
    }
  } catch (e) {
    print('Ошибка: $e');
  }
}

4. Загрузка нескольких файлов

import 'package:http/http.dart' as http;
import 'dart:io';

Future<void> uploadMultipleFiles(List<File> files) async {
  var request = http.MultipartRequest(
    'POST',
    Uri.parse('https://api.example.com/upload-multiple'),
  );
  
  // Добавляем несколько файлов
  for (var file in files) {
    request.files.add(
      await http.MultipartFile.fromPath(
        'files',  // Одно имя для всех файлов
        file.path,
      ),
    );
  }
  
  try {
    var response = await request.send();
    
    if (response.statusCode == 200) {
      var responseBody = await response.stream.bytesToString();
      print('Ответ: $responseBody');
    }
  } catch (e) {
    print('Ошибка: $e');
  }
}

5. Выбор и загрузка файла с помощью file_picker

import 'package:file_picker/file_picker.dart';
import 'package:http/http.dart' as http;
import 'dart:io';

Future<void> pickAndUploadFile() async {
  try {
    FilePickerResult? result = await FilePicker.platform.pickFiles(
      type: FileType.any,
      allowMultiple: false,
    );
    
    if (result != null) {
      File file = File(result.files.single.path!);
      await uploadFile(file);
    } else {
      print('Пользователь отменил выбор файла');
    }
  } catch (e) {
    print('Ошибка: $e');
  }
}

6. JSON запрос с файлом в виде Base64

Для отправки файла как часть JSON:

import 'package:http/http.dart' as http;
import 'dart:io';
import 'dart:convert';

Future<void> uploadFileAsBase64(File file) async {
  try {
    // Читаем файл и кодируем в Base64
    List<int> fileBytes = await file.readAsBytes();
    String base64File = base64Encode(fileBytes);
    
    Map<String, dynamic> payload = {
      'file': base64File,
      'fileName': file.path.split('/').last,
      'userId': '123',
    };
    
    var response = await http.post(
      Uri.parse('https://api.example.com/upload'),
      headers: {
        'Content-Type': 'application/json',
        'Authorization': 'Bearer token',
      },
      body: jsonEncode(payload),
    );
    
    if (response.statusCode == 200) {
      print('Файл загружен');
    }
  } catch (e) {
    print('Ошибка: $e');
  }
}

7. Загрузка с обработкой ошибок и retry

import 'package:http/http.dart' as http;
import 'dart:io';

Future<void> uploadFileWithRetry(
  File file, {
  int maxRetries = 3,
}) async {
  int retryCount = 0;
  
  while (retryCount < maxRetries) {
    try {
      var request = http.MultipartRequest(
        'POST',
        Uri.parse('https://api.example.com/upload'),
      );
      
      request.files.add(
        await http.MultipartFile.fromPath('file', file.path),
      );
      
      var response = await request.send().timeout(
        Duration(seconds: 30),
        onTimeout: () {
          throw TimeoutException('Превышено время ожидания');
        },
      );
      
      if (response.statusCode == 200) {
        print('Файл успешно загружен');
        return;
      } else if (response.statusCode >= 500) {
        // Ошибка сервера, пробуем снова
        retryCount++;
        print('Повтор попытки $retryCount...');
        await Future.delayed(Duration(seconds: 2 * retryCount));
      } else {
        // Ошибка клиента, не повторяем
        print('Ошибка клиента: ${response.statusCode}');
        return;
      }
    } catch (e) {
      retryCount++;
      if (retryCount < maxRetries) {
        print('Ошибка, повтор попытки $retryCount: $e');
        await Future.delayed(Duration(seconds: 2 * retryCount));
      } else {
        print('Все попытки исчерпаны: $e');
      }
    }
  }
}

Сравнение подходов

ПодходИспользованиеПлюсыМинусы
http + MultipartRequestСтандартная загрузкаВстроенный пакетНет отслеживания прогресса
http + BinaryJSON APIПростоБольшие файлы медленнее
dioБольшие файлыПрогресс, retryДополнительный пакет
Base64 + JSONМаленькие файлыУниверсальноМедленнее, больший трафик

Лучшие практики

1. Проверяйте размер файла:

const maxFileSize = 10 * 1024 * 1024;  // 10 MB
if (file.lengthSync() > maxFileSize) {
  print('Файл слишком большой');
  return;
}

2. Используйте тайм-ауты:

var response = await request.send().timeout(
  Duration(seconds: 60),
  onTimeout: () => throw Exception('Timeout'),
);

3. Обрабатывайте ошибки сети:

try {
  // загрузка
} on SocketException {
  print('Нет интернета');
} on TimeoutException {
  print('Превышено время');
} catch (e) {
  print('Другая ошибка');
}

4. Показывайте прогресс пользователю: Используйте dio с onSendProgress для отслеживания загрузки и обновления UI через StreamBuilder или BLoC.