Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

zaster's avatar

API - Image gets uploaded with Zero Size

I am using Laravel Sanctum as the API and Flutter as the frontend.

The image is getting uploaded with Zero Size and i am trying to figure out whether it is an issue at the Laravel API side or the frontend(Flutter)

Anybody have any experience going through this kind of an issue ?

 public function store(Request $request)
    {
        //validate fields
        $attrs = $request->validate([
            'body' => 'required|string'
        ]);

        $image = $this->saveImage($request->image, 'posts');

        $post = Post::create([
            'body' => $attrs['body'],
            'user_id' => auth()->user()->id,
            'image' => $image
        ]);


        return response([
            'message' => 'Post created',
            'post' => $post
        ], 200);
    }

0 likes
14 replies
tykus's avatar

How are you uploading the image; are you sending a base64_encoded image; or is the API not only handling application/json?

zaster's avatar

@tykus Sending base64_encoded image

This code if from the client side(Flutter)

// Get base64 encoded image
String? getStringImage(File? file) {
  if (file == null) return null ;
  return base64Encode(file.readAsBytesSync());
}	
tykus's avatar

@zaster okay so you are sending a String, so I suppose the payload is JSON, what does that look like? And how are you handling that image in saveImage method?

zaster's avatar

@tykus

The paylod is JSON and the flutter code looks like this

lib\models\post.dart

class Post {
.
.
.
factory Post.fromJson(Map<String, dynamic> json) {
  return Post(
    id: json['id'],
    body: json['body'],
    image: json['image'],
    likesCount: json['likes_count'],
    commentsCount: json['comments_count'],
    selfLiked: json['likes'].length > 0,
    user: User(
      id: json['user']['id'],
      name: json['user']['name'],
      image: json['user']['image']
    )
  );
 }
}

lib\screens\post_form.dart

class _PostFormState extends State<PostForm> {
  final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
  final TextEditingController _txtControllerBody = TextEditingController();
  bool _loading = false;
   File? _imageFile;
  final _picker = ImagePicker();

  Future getImage() async {
    final pickedFile = await _picker.pickImage(source: ImageSource.gallery);

    
    if (pickedFile != null){
      setState(() {
        _imageFile = File(pickedFile.path);
      });
    }
  }

  void _createPost() async {
    String? image = _imageFile ==  null ? null : getStringImage(_imageFile);
    ApiResponse response = await createPost(_txtControllerBody.text, image);

    if(response.error ==  null) {
      if(!mounted) return;
      Navigator.of(context).pop();
    }
    else if (response.error == unauthorized){
      logout().then((value) => {
        Navigator.of(context).pushAndRemoveUntil(MaterialPageRoute(builder: (context)=>const Login()), (route) => false)
      });
    }
    else {
      if(!mounted) return;
      ScaffoldMessenger.of(context).showSnackBar(SnackBar(
        content: Text('${response.error}')
      ));
      setState(() {
        _loading = !_loading;
      });
    }
  }

lib\services\post_service.dart


// Create post
Future<ApiResponse> createPost(String body, String? image) async {
  ApiResponse apiResponse = ApiResponse();
  try {
    String token = await getToken();
    final response = await http.post(Uri.parse(postsURL),
    headers: {
      'Accept': 'application/json',
      'Authorization': 'Bearer $token'
    }, body: image !=null ? {
      'body': body,
      'image': image
    } : {
      'body': body
    });

    // here if the image is null we just send the body, if not null we send the image too

    switch(response.statusCode){
      case 200:
        apiResponse.data = jsonDecode(response.body);
        break;
      case 422:
        final errors = jsonDecode(response.body)['errors'];
        apiResponse.error = errors[errors.keys.elementAt(0)][0];
        break;
      case 401:
        apiResponse.error = unauthorized;
        break;
      default:
        // ignore: avoid_print
        print(response.body);
        apiResponse.error = somethingWentWrong;
        break;
    }
  }
  catch (e){
    apiResponse.error = serverError;
  }
  return apiResponse;
}

Basically, i am trying to make this work

https://github.com/habibmhamadi/flutter-blog-app/tree/main/lib

https://github.com/habibmhamadi/laravel8-blog-api

https://www.youtube.com/playlist?list=PL0qWGthGFUCjFDgYI2k_-TqMNA7925c1s

zaster's avatar

@tykus

oops! Sorry.

The laravel code looks like this

app\Http\Controllers\PostController.php

//create a post
    public function store(Request $request)
    {
        //validate fields
        $attrs = $request->validate([
            'body' => 'required|string'
        ]);

        $image = $this->saveImage($request->image, 'posts');

        $post = Post::create([
            'body' => $attrs['body'],
            'user_id' => auth()->user()->id,
            'image' => $image
        ]);


        return response([
            'message' => 'Post created',
            'post' => $post
        ], 200);
    }

app\Http\Controllers\Controller.php

.
.
class Controller extends BaseController
{
    use AuthorizesRequests, DispatchesJobs, ValidatesRequests;

    public function saveImage($image, $path = 'public')
    {
        if($image)
        {
            return null;
        }

        $filename = time().'.png';
        //save image
        \Storage::disk($path)->put($filename, base64_decode($image));

        //return the path
        // Url is the base url exp: localhost:8000
        return URL::to('/').'/storage/'.$path.'/'.$filename;


    }
}

app\Models\Post.php

.
.
protected $fillable = [
        'user_id',
        'body',
        'image'
    ];

tykus's avatar

@zaster does the base64 encoded string in the form of a data URL; or is it pure Base 64 encoding? Can you post a sample base64 encoded string here (something that is not sensitive or NSFW!)?

zaster's avatar

@tykus

I have tested this in both local and production environment.

Local Environment - Tested using Postman

This is when only body parameter is given (No image uploaded). It is the same when an image is uploaded

Url : http://blog-api.test/api/posts?body=test

JSON

{
    "message": "Post created",
    "post": {
        "body": "test",
        "user_id": 2,
        "image": "http://blog-api.test/storage/posts/1675852471.png",
        "updated_at": "2023-02-08T10:34:31.000000Z",
        "created_at": "2023-02-08T10:34:31.000000Z",
        "id": 7
    }
}
tykus's avatar

@zaster that looks like the result was successful? Anyway, I asked about the base64 encoded data you are sending to the application; not the result!

zaster's avatar

@tykus

Form submission with image

JSON

{
    "message": "Post created",
    "post": {
        "body": "test",
        "user_id": 2,
        "image": null,
        "updated_at": "2023-02-08T14:57:42.000000Z",
        "created_at": "2023-02-08T14:57:42.000000Z",
        "id": 14
    }
}

Telescope - Log

Nothing

Form Submission with NO image

JSON

{
    "message": "Post created",
    "post": {
        "body": "test",
        "user_id": 2,
        "image": "http://blog-api.test/storage/posts/1675868588.png",
        "updated_at": "2023-02-08T15:03:08.000000Z",
        "created_at": "2023-02-08T15:03:08.000000Z",
        "id": 15
    }
}

Telescope - Log

base64_decode(): Passing null to parameter #1 ($string) of type string is deprecated in C:\web\blog-api\app\Http\Controllers\Controller.php on line 26

This is Line 26

 \Storage::disk($path)->put($filename, base64_decode($image));
tykus's avatar

@zaster I am not interested in the result; I wanted to know the payload from Flutter

zaster's avatar

@tykus

I have used Postman as the client (Instead of flutter becuase there might be issues with the emulator etc...)

and then included the Telescope - Log

Let me give it another try

Please or to participate in this conversation.