Downloading Files from an API

Welcome back! Today's focus will be on downloading files from an API using Dart. Understanding how to retrieve files efficiently not only enhances your technical skills but also broadens your application's capabilities. In this lesson, we'll explore a practical scenario using our To-Do list API, which, in addition to managing tasks, supports handling text files such as notes. These notes can be downloaded or uploaded through the /notes endpoint, allowing functionality for storing supplementary information. For example, users might keep notes about a meeting or important reminders. By understanding how to interact with this endpoint, you can effectively manage notes within your application. By the end of this lesson, you'll know how to request a file from an API, save it locally, and verify its contents.

Let's dive into downloading files with precision and confidence!

Basic File Download with GET Requests

GET requests are fundamental for retrieving files from an API. When you send a GET request using Dart's http.get(), your client communicates with the server at a specified URL, asking it to provide the file. The server responds with the file data, if available and permissible, along with an HTTP status code (like 200 OK).

Here's a basic example of downloading a file named welcome.txt from our API at http://localhost:8000/notes. This approach downloads the entire file at once, which is manageable for smaller files.

Dart
1import 'dart:io'; 2import 'dart:convert'; 3import 'package:http/http.dart' as http; 4 5void main() async { 6 // Base URL for the API 7 final String baseUrl = "http://localhost:8000"; 8 9 // Specify the note name to download 10 final String noteName = "welcome.txt"; 11 12 try { 13 // Send a GET request to download the file 14 final response = await http.get(Uri.parse('$baseUrl/notes/$noteName')); 15 16 // Check for bad responses (4xx and 5xx status codes) 17 if (response.statusCode >= 400) { 18 String errorMessage = jsonDecode(response.body)['error'] ?? 'Unknown error'; 19 throw http.ClientException(errorMessage); 20 } 21 22 // Save the file locally 23 final file = File("downloaded_$noteName"); 24 await file.writeAsBytes(response.bodyBytes); 25 26 // Print success message if file is downloaded successfully 27 print("Note downloaded successfully: downloaded_$noteName"); 28 } on http.ClientException catch (httpErr) { 29 print(httpErr.message); 30 } catch (err) { 31 print("Other error occurred: $err"); 32 } 33}

This code sends a GET request and writes the full response content to a local file. This method works well for small files but can strain memory for larger files.

Leveraging GET Requests and Streaming

When dealing with large files, downloading them all at once can be inefficient and strain memory. To address this, Dart allows you to handle streaming using http.Request and response.stream.pipe(file). This approach optimizes memory usage and maintains efficiency by downloading files in chunks.

Here's a step-by-step breakdown of how to modify the basic GET request to use streaming:

  1. Create an HTTP Request: Instead of using http.get(), we use http.Request to create a request object. This allows us to handle the response as a stream.

    Dart
    1final request = http.Request('GET', Uri.parse('$baseUrl/notes/$noteName'));
  2. Send the Request and Receive a Streamed Response: Use request.send() to send the request. This returns a StreamedResponse object, which provides access to the response stream.

    Dart
    1final response = await request.send();
  3. Open a File for Writing: Before piping the stream, open a file in write mode. This is where the streamed data will be saved.

    Dart
    1final file = File(fileName).openWrite();
  4. Pipe the Response Stream to the File: Use response.stream.pipe(file) to transfer the data from the response stream directly into the file. This method handles the data in chunks, reducing memory usage.

    Dart
    1await response.stream.pipe(file);
  5. Close the File: After the streaming is complete, close the file to ensure all data is written and resources are released.

    Dart
    1await file.close();

By utilizing streaming, even large files are downloaded efficiently. This technique is especially useful when file sizes increase.

Complete Streaming Example

Below is the complete code example demonstrating how to download the file using streaming:

Dart
1import 'dart:io'; 2import 'dart:convert'; 3import 'package:http/http.dart' as http; 4 5void main() async { 6 // Base URL for the API 7 final String baseUrl = "http://localhost:8000"; 8 // Specify the note name to download 9 final String noteName = "welcome.txt"; 10 11 try { 12 // Create an HTTP request 13 final request = http.Request('GET', Uri.parse('$baseUrl/notes/$noteName')); 14 // Send the request and get a streamed response 15 final response = await request.send(); 16 // Check for bad responses (4xx and 5xx status codes) 17 if (response.statusCode >= 400) { 18 String errorMessage = jsonDecode(response.body)['error'] ?? 'Unknown error'; 19 throw http.ClientException(errorMessage); 20 } 21 // Define the filename for storing the downloaded content 22 final String fileName = "downloaded_$noteName"; 23 // Open a file for writing 24 final file = File(fileName).openWrite(); 25 // Pipe the response stream to the file 26 await response.stream.pipe(file); 27 // Close the file 28 await file.close(); 29 // Print success message if file is downloaded successfully 30 print("Note downloaded successfully: $fileName"); 31 } on http.ClientException catch (httpErr) { 32 print(httpErr.message); 33 } catch (err) { 34 print("Other error occurred: $err"); 35 } 36}
Verification of Downloaded File Content

Once you've downloaded a file, it's imperative to verify its contents to ensure a successful transfer. In our example, after downloading, you can open the file and print its content to confirm data integrity:

Dart
1final String noteName = "welcome.txt"; 2final String fileName = "downloaded_$noteName"; 3 4try { 5 // Read and print the content of the downloaded file 6 final content = await File(fileName).readAsString(); 7 print("Content of the downloaded file:\n"); 8 print(content); 9} catch (err) { 10 // Handle file errors 11 print("File error occurred: $err"); 12}

If everything is functioning correctly, you should see an output similar to:

Plain text
1Welcome to Your Notes! 📝 2 3This is a sample note that comes with the application.

This step is essential for data verification. The familiar error-handling techniques come into play once more, using try-catch blocks to gracefully address any issues during the download and verification process.

Summary and Preparation for Practice

In this lesson, you explored two methods for downloading files from an API: a straightforward approach for smaller files and a more efficient streaming method for larger files. You've practiced verifying file integrity by reading its contents post-download and reinforced your knowledge of error management. As you proceed to the practice exercises, you'll have the opportunity to apply these skills, reinforcing your ability to manage API interactions effectively. Keep experimenting with different files and settings, as this will further enhance your understanding and proficiency. Exciting topics await, such as file uploads and handling paginated responses. Your journey in mastering API interactions continues!

Sign up
Join the 1M+ learners on CodeSignal
Be a part of our community of 1M+ users who develop and demonstrate their skills on CodeSignal