[Fixed] FileSystemException: Cannot retrieve length of file, path ='...' (OS Error: No such file or directory, errno = 2)


Recently, I started learning Flutter, and I'm attempting to send a user model from Dart to an ASP.NET Core API and  my file is set as an IFormFile data type, and when I tried using the multipart request, I initially encountered the error "FileSystemException: Cannot retrieve the length of the file." If you're also facing the same issue, then this post is for you.
Let's undersatnd with an example, and for  have cerated an ASP.NET Core API endpoint for uploading user details along with a company logo,  Let's create a controller named UserDetailController:
using Microsoft.AspNetCore.Mvc;
    using Microsoft.AspNetCore.Http;
    using System.IO;
    using System.Threading.Tasks;

    [Route("api/[controller]")]
    [ApiController]
    public class UserDetailController : ControllerBase
    {
        [HttpPost("upload")]
        public async Task<IActionResult> UploadUserDetails([FromForm] UserDetailRequest request)
        {
            if (request == null || request.CompanyLogo == null || request.CompanyLogo.Length <= 0)
            {
                return BadRequest("Invalid request");
            }

            // Save user details to the database (assuming you have a database setup)

            // Save company logo to the assets folder
            var assetsDirectory = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "assets");
            if (!Directory.Exists(assetsDirectory))
            {
                Directory.CreateDirectory(assetsDirectory);
            }

            var logoFileName = $"{request.Id}_logo.png"; // Example: 1_logo.png
            var logoFilePath = Path.Combine(assetsDirectory, logoFileName);

            using (var stream = new FileStream(logoFilePath, FileMode.Create))
            {
                await request.CompanyLogo.CopyToAsync(stream);
            }

            // Return success response
            return Ok("User details and company logo uploaded successfully.");
        }
    }

    public class UserDetailRequest
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string EmailId { get; set; }
        public IFormFile CompanyLogo { get; set; }
    }
    

UserDetailController controller having a POST endpoint /api/userdetail/upload that accepts user details including Id, Name, EmailId, and CompanyLogo as multipart form data.

In Flutter App for Updating User Details

Now, let's write a Flutter application client code to upload user details along with the company logo image.

import 'dart:typed_data';
    import 'package:flutter/material.dart';
    import 'package:http/http.dart' as http;
    import 'package:flutter/services.dart' show rootBundle;

    class UserDetailsUploader extends StatefulWidget {
      @override
      _UserDetailsUploaderState createState() => _UserDetailsUploaderState();
    }

    class _UserDetailsUploaderState extends State<UserDetailsUploader> {
      int _userId;
      String _name;
      String _emailId;
      Uint8List _companyLogoBytes;

      Future<void> _uploadUserDetails() async {
        var request = http.MultipartRequest('POST', Uri.parse('https://www.quickpickdeal.com/api/userdetail/upload'));
        request.fields['id'] = _userId.toString();
        request.fields['name'] = _name;
        request.fields['emailId'] = _emailId;
        request.files.add(http.MultipartFile.fromBytes(
          'companyLogo',
          _companyLogoBytes,
          filename: 'company_logo.png',
          contentType: MediaType('image', 'png'),
        ));

        var response = await request.send();
        if (response.statusCode == 200) {
          // User details uploaded successfully
          print('User details uploaded');
        } else {
          // Handle error
          print('Failed to upload user details');
        }
      }

      @override
      void initState() {
        super.initState();
        _loadCompanyLogo();
      }

      Future<void> _loadCompanyLogo() async {
        final ByteData data = await rootBundle.load('assets/company_logo.png');
        _companyLogoBytes = data.buffer.asUint8List();
      }

      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text('User Details Uploader'),
          ),
          body: Padding(
            padding: const EdgeInsets.all(20.0),
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                TextField(
                  decoration: InputDecoration(labelText: 'User ID'),
                  keyboardType: TextInputType.number,
                  onChanged: (value) {
                    setState(() {
                      _userId = int.tryParse(value);
                    });
                  },
                ),
                TextField(
                  decoration: InputDecoration(labelText: 'Name'),
                  onChanged: (value) {
                    setState(() {
                      _name = value;
                    });
                  },
                ),
                TextField(
                  decoration: InputDecoration(labelText: 'Email ID'),
                  onChanged: (value) {
                    setState(() {
                      _emailId = value;
                    });
                  },
                ),
                ElevatedButton(
                  onPressed: () async {
                    if (_userId != null && _name != null && _emailId != null && _companyLogoBytes != null) {
                      await _uploadUserDetails();
                    }
                  },
                  child: Text('Upload User Details'),
                ),
              ],
            ),
          ),
        );
      }
    }
    

UserDetailsUploader widget that allows users to enter user details and upload a company logo image and the company logo image is loaded from the assets folder using rootBundle and then converted to bytes for uploading.

In above we are importing rootBundle from package:flutter/services.dart to access the application's asset bundle and the I have defined a StatefulWidget called UserDetailsUploader. UserDetailsUploader widget uses a form where users can input their details and upload them to a server and inside this widget, I have a private _UserDetailsUploaderState class that extends State.

Within _UserDetailsUploaderState, I declare variables to hold user details such as _userId, _name, _emailId, and _companyLogoBytes, which will store the byte data of the company logo image.

The _uploadUserDetails function is used for HTTP POST request to upload user details to the endpoint. On the button click, we check if all required fields are filled (_userId, _name, _emailId, _companyLogoBytes), and if so, I invoke _uploadUserDetails to initiate the upload process.

ASP.NET Core API Endpoint for Image Upload Using Flutter

we first need to set up a controller to handle the HTTP requests. Let's create a controller named ImageController:

using Microsoft.AspNetCore.Mvc;
    using Microsoft.AspNetCore.Http;
    using System.IO;
    using System.Threading.Tasks;

    [Route("api/[controller]")]
    [ApiController]
    public class ImageController : ControllerBase
    {
        [HttpPost("upload")]
        public async Task<IActionResult> UploadImage(IFormFile file)
        {
            if (file == null || file.Length <= 0)
            {
                return BadRequest("Invalid file");
            }

            // Define the directory to save the uploaded file
            var uploadsDirectory = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "uploads");

            // Ensure the directory exists
            if (!Directory.Exists(uploadsDirectory))
            {
                Directory.CreateDirectory(uploadsDirectory);
            }

            // Generate a unique filename for the uploaded file
            var fileName = Path.GetRandomFileName();
            var filePath = Path.Combine(uploadsDirectory, fileName);

            // Copy the uploaded file to the specified location
            using (var stream = new FileStream(filePath, FileMode.Create))
            {
                await file.CopyToAsync(stream);
            }

            // Return the URL of the uploaded file
            var baseUrl = $"{Request.Scheme}://{Request.Host}";
            var imageUrl = $"{baseUrl}/uploads/{fileName}";
            
            return Ok(new { imageUrl });
        }
    }
    

This ImageController controller having a single HTTP POST endpoint /api/image/upload that accepts an image file as IFormFile. It saves the uploaded image to the wwwroot/uploads directory and returns the URL of the uploaded image.

Flutter Multipart Request

Now, let's see how we can use this API endpoint to upload images from a Flutter application using multipart requests. We'll use the http package in Flutter for making HTTP requests.

First, ensure you have added the http package to your pubspec.yaml:

dependencies:
      flutter:
        sdk: flutter
      http: ^0.14.0
    

Now, you can use the following Flutter code to upload an image:

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

    class ImageUploader extends StatefulWidget {
      @override
      _ImageUploaderState createState() => _ImageUploaderState();
    }

    class _ImageUploaderState extends State<ImageUploader> {
      File _imageFile;

      Future<void> _uploadImage(File image) async {
        var request = http.MultipartRequest('POST', Uri.parse('https://www.quickpickdeal.com/api/image/upload'));
        request.files.add(await http.MultipartFile.fromPath('file', image.path));
        
        var response = await request.send();
        if (response.statusCode == 200) {
          // Image uploaded successfully
          print('Image uploaded');
        } else {
          // Handle error
          print('Failed to upload image');
        }
      }

      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text('Image Uploader'),
          ),
          body: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                _imageFile == null
                    ? Text('No image selected.')
                    : Image.file(_imageFile),
                ElevatedButton(
                  onPressed: () async {
                    // Open image picker
                    // code to select image and assign to _imageFile
                    // Example: _imageFile = await ImagePicker.pickImage(source: ImageSource.gallery);
                    if (_imageFile != null) {
                      await _uploadImage(_imageFile);
                    }
                  },
                  child: Text('Select Image'),
                ),
              ],
            ),
          ),
        );
      }
    }