Introduction to Strapi Media Library

Ekekenta Odionyenfe Clinton - May 31 - - Dev Community

Strapi is an open-source, Node.js-based, Headless Content Management System(CMS) that allows you to streamline the process of managing content, giving you the freedom to use your favorite tools and frameworks. It has a Media Library plugin that allows you to upload, save, and manage different media files such as images, videos, and documents.

In this tutorial, you'll learn how to build a media gallery application using Flutter and Strapi's Media Library plugin.

Prerequisites

You will need the following to get started:

The code for this tutorial is available on my GitHub repository. Feel free to clone and follow along.

Setting up Strapi with the Media Library

Strapi Installation

Start by creating a new Strapi backend project by running the command below:



npx create-strapi-app@latest media-app --quickstart


Enter fullscreen mode Exit fullscreen mode

The above command will scaffold a new Strapi content management system project and install the required Node.js dependencies.

Fill out the forms to create your administrator user account.

Install Strapi Media Library Plugin

Go back to your terminal, and quit the server with Ctrl+ C. Navigate to the project directory and enable the Media Libray Plugin by running the command below:



cd media-app
npm i @strapi/plugin-upload


Enter fullscreen mode Exit fullscreen mode

Update your config/plugin.js file to configure the upload plugin. There are many plugins in the Strapi marketplace for uploads including Firebase storage, Cloudflare, etc. But there are only 3 providers maintained by Strapi which are Local, Amazon S3, and Cloudinary. For this demonstration, you'll use the Local plugin:



module.exports = ({ env }) => ({
  upload: {
    config: {
      providerOptions: {
        localServer: {
          maxage: 300000,
        },
      },
    },
  },
});



Enter fullscreen mode Exit fullscreen mode

Enable Public Access for Upload API

In order to be able to perform some operations on the upload plugin, we need to enable public access.

From the left sidebar, click on Settings. Again, on the left panel under USERS & PERMISSIONS PLUGIN, click on Roles, then click on Public from the table on the right. Now scroll down, click on Upload, and tick Select all. After that click the "Save" button.

001-enable-api-access.png

Uploading Media Files to Strapi

Back to your admin panel, in the left sidebar, click on the Media Library section. Then click Add new assets -> FROM COMPUTER -> Browse Files and you'll be prompted to upload files from your local computer. Select at least three files and click Upload assets to library to upload them.

Creating a Flutter Project

Open a new tab on your terminal and create a new Flutter application by running the command below:



flutter create media_library_app


Enter fullscreen mode Exit fullscreen mode

Once the project is created, open it in your IDE or code editor. Change the directory to the project folder and run the application with the following commands:



cd media_library_app
flutter run


Enter fullscreen mode Exit fullscreen mode

Retrieving Media Files from Flutter

In your projects lib directory, create a features/services directory. Create a strapi_service.dart file in the services directory and add the code to fetch all the assets you added to your Media Library:



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

class StrapiService {
  static const String baseUrl = 'http://localhost:1337';

  Future<List<dynamic>> getMediaFiles() async {
    final response = await http.get(Uri.parse('$baseUrl/api/upload/files'));
    if (response.statusCode == 200) {
      final jsonData = json.decode(response.body);
      return jsonData;
    } else {
      throw Exception('Failed to load media files');
    }
  }
}



Enter fullscreen mode Exit fullscreen mode

This service class provides a getMediaFiles method that makes a GET request to the Strapi server's /upload/files endpoint to return the list of media files.

For the above code to work, open the pubspec.yaml file and add the http, file_picker and http_parser module in the dependency section to allow you send API requests to your Strapi backend, select images from your device file system and to parse the HTTP response:



dependencies:
  flutter:
    sdk: flutter
  http: ^0.13.5
  file_picker: ^8.0.3
  http_parser: ^4.0.2


Enter fullscreen mode Exit fullscreen mode

Then run the command below to install the dependency:



flutter pub get


Enter fullscreen mode Exit fullscreen mode

Displaying Media Files in Flutter

Create a new directory called screens in the lib/features directory to create our Flutter UI. Then create a home_screen.dart file inside the screens folder and add the code snippets below:



import 'package:flutter/material.dart';
import 'package:media_library_app/features/services/strapi_service.dart';

class HomePage extends StatefulWidget {
  const HomePage({super.key});

  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  List<dynamic> mediaFiles = [];
  StrapiService strapiService = StrapiService();

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

  Future<void> fetchMediaFiles() async {
    try {
      final files = await strapiService.getMediaFiles();
      setState(() {
        mediaFiles = files;
      });
    } catch (e) {
      print('Error fetching media files: $e');
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Strapi Media Library'),
      ),
      body: Padding(
        padding: const EdgeInsets.all(8.0),
        child: ListView.builder(
          itemCount: mediaFiles.length,
          itemBuilder: (context, index) {
            final file = mediaFiles[index];
            final imageUrl = StrapiService.baseUrl + file['url'];
            return ListTile(
              title: Text(file['name']),
              subtitle: Text(file['mime']),
              leading: Image.network(imageUrl),
            );
          },
        ),
      ),
    );
  }
}


Enter fullscreen mode Exit fullscreen mode

The above code will:

  • Create a HomePage StatefulWidget class to allow you to handle the application state on this screen.
  • Create mediaFiles empty array to store the media files data and create an instance of the StrapService class to allow you access to the methods in the class.
  • Add a fetchMediaFiles() method to call the strapiService.getMediaFiles() method to retrieve the media from Strapi Media Library and call the setState function to modify the app's state and set the retrieved files data to the mediaFiles array.
  • Call the initState method to execute the code in fetchMediaFiles() when the widget is first created.
  • Render the media data using the ListView widget.

Now update your main.dart file to render the HomePage widget in your Flutter UI:



import 'package:flutter/material.dart';
import 'package:strapi_media_app/features/screens/home_screen.dart';

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

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

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const HomePage(),
    );
  }
}



Enter fullscreen mode Exit fullscreen mode

002-image-media-library.png

Handling Media Uploads from Flutter

Next, update the code in your lib/features/services/strapi_service.dart file to add a new method to send a POST request to the /upload endpoint to upload new files to the Media Library.



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

class StrapiService {
  static const String baseUrl = 'http://localhost:1337';

  Future<List<dynamic>> getMediaFiles() async {
    try {
      final response = await http.get(Uri.parse('$baseUrl/api/upload/files'));
      if (response.statusCode == 200) {
        final jsonData = json.decode(response.body);
        return jsonData;
      } else {
        throw Exception('Failed to load media files');
      }
    } catch (e) {
      throw Exception(e);
    }
  }

  Future<dynamic> uploadMediaFile(File file) async {
    var request = http.MultipartRequest(
      'POST',
      Uri.parse('$baseUrl/api/upload'),
    );
    var stream = http.ByteStream(file.openRead());
    stream.cast();
    var length = await file.length();
    var multipartFileSign = http.MultipartFile(
      'files',
      stream,
      length,
      filename: file.path.split('/').last,
      contentType: MediaType('image', 'jpeg'),
    );

    request.files.add(multipartFileSign);

    var response = await request.send();
    if (response.statusCode == 200) {
      return response.stream.bytesToString();
    } else {
      throw Exception('Failed to upload media file');
    }
  }
}



Enter fullscreen mode Exit fullscreen mode

This code will:

  • Create an HTTP request to allow you upload files using http.MultipartRequest to the /upload endpoint using the POST method.
  • Prepare the selected files for your upload by opening a byte stream using http.ByteStream from the file and specifying the length.
  • Creates a multipart file using http.MultipartFile from the byte stream, specifying the file name, length, content type, and field name.
  • Add the multipart file to the request. If the response status code is 200 (OK), it reads the response stream and converts it to a string using response.stream.bytesToString(), then returns the result.

Then in your lib/features/screens/home_screen.dart file, add a new method to the _HomePageState class to handle file uploads.



 import 'dart:convert';
import 'dart:io';
// import 'package:file_picker/file_picker.dart';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import 'package:strapi_media_app/features/services/strapi_service.dart';

class HomePage extends StatefulWidget {
  const HomePage({super.key});

  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  List<dynamic> mediaFiles = [];
  StrapiService strapiService = StrapiService();

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

  Future<void> fetchMediaFiles() async {
    try {
      final files = await strapiService.getMediaFiles();
      setState(() {
        mediaFiles = files;
      });
    } catch (e) {
      print('Error fetching media files: $e');
    }
  }

  Future<void> uploadMediaFile(context) async {
    FilePickerResult? result = await FilePicker.platform.pickFiles();
    if (result != null) {
      File file = File(result.files.single.path!);
      try {
        final response = await strapiService.uploadMediaFile(file);
        List<dynamic> jsonResponse = jsonDecode(response);
        setState(() {
          mediaFiles.add(jsonResponse[0]);
        });
        ScaffoldMessenger.of(context).showSnackBar(
          const SnackBar(
            content: Text('File uploaded successfully'),
          ),
        );
      } catch (e) {
        ScaffoldMessenger.of(context).showSnackBar(
          SnackBar(
            content: Text('Error uploading media file: $e'),
          ),
        );
      }
    } else {
      ScaffoldMessenger.of(context).showSnackBar(
        const SnackBar(
          content: Text('No file selected'),
        ),
      );
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Strapi Media Library'),
      ),
      body: Padding(
        padding: const EdgeInsets.all(8.0),
        child: ListView.builder(
          itemCount: mediaFiles.length,
          itemBuilder: (context, index) {
            final file = mediaFiles[index];
            final imageUrl = StrapiService.baseUrl + file['url'];
            return ListTile(
              title: Text(file['name']),
              subtitle: Text(file['mime']),
              leading: Image.network(imageUrl),
            );
          },
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () => uploadMediaFile(context),
        child: const Icon(Icons.add),
      ),
    );
  }
}



Enter fullscreen mode Exit fullscreen mode

The above code will:

  • Create an uploadMediaFile method in the _HomePageState class to call the uploadMediaFile method from the StrapiService class to upload the selected files to the Strapi Media Library.
  • Uses the FilePicker package to allow the user to select a file from their device. If the file selected is not null, it will retrieve the selected file and prepares it for upload, else it shows a snackbar with a success or error message.

003-image-upload.png

Demo

Below is a demonstration of what our application looks like.
demofileupload-ezgif.com-optimize.gif

Conclusion

Throughout this tutorial, you've learned how to build a media gallery application using Flutter and Strapi Media Library. You have set up a Strapi project, installed the Strapi upload plugin in the project, and uploaded media data to the Strapi Media Library. You created a Flutter application to make API requests to the Media Libray to fetch, display, and upload new files.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .