Recently, I created a REST API for uploading and retrieving files using Spring Boot and Firebase. This project was driven by my interest after seeing someone mention they were having issues with file uploads in Spring Boot. In an effort to help, I decided to familiarize myself with file uploads using Spring Boot and Firebase.
So, if you’re like me and want to get familiar with file management using Firebase and Spring Boot, let’s get started! 😊
First Up: Setting Up Firebase 🛠️
Before we dive into the code, the first step is setting up Firebase to handle file uploads.
Step 1: Create a Firebase Project
- Go to the Firebase Console: Firebase Console
- Create a New Project:
- Enter a project name.
- Click "Continue" and select whether to enable Google Analytics (optional).
- Click "Create Project."
Step 2: Enable Cloud Storage
- Open Your Project: In the Firebase console, select your project.
- Navigate to Cloud Storage:
- Click on “Build > Storage” in the left sidebar.
- Click on “Get Started” and "Start in test mode (or production if needed)" to enable Cloud Storage for your project.
Step 3: Get Your Service Account Key 🔑
- Go to "Project Settings" > "Service accounts."
- Click on “Generate new private key,” and a JSON file will be downloaded. Keep this file secure.
That’s it for Firebase setup! Now, let’s move on to integrating it with Spring Boot.
Next: Setting Up Spring Boot ⚙️
1. Create a Spring Boot Project
You can create a Spring Boot project using Spring Initializr.
Select the following dependencies:
- Spring Web: For building REST APIs.
- Spring Boot DevTools (optional, for easier development).
2. Add Firebase Dependencies
Add the Firebase dependency to your pom.xml
:
<dependency>
<groupId>com.google.firebase</groupId>
<artifactId>firebase-admin</artifactId>
<version>8.1.0</version>
</dependency>
3. Firebase Configuration
You need to set up Firebase authentication using the .json
key file you downloaded earlier.
- Place the
.json
file in yoursrc/main/resources
directory. - Then, create a configuration class
FirebaseConfig
to initialize Firebase:
@Configuration
public class FirebaseConfig {
@Bean
FirebaseApp firebaseApp() throws IOException {
ClassPathResource resource = new ClassPathResource("serviceAccountKey.json");
InputStream serviceAccount = resource.getInputStream();
FirebaseOptions options = FirebaseOptions.builder()
.setProjectId("fir-fileuploadapi")
.setCredentials(GoogleCredentials.fromStream(serviceAccount))
.setStorageBucket("fir-fileuploadapi.appspot.com")
.setDatabaseUrl("https://fir-fileuploadapi-default-rtdb.firebaseio.com")
.build();
return FirebaseApp.initializeApp(options);
}
}
Here, replace the projectId
, bucketName
, and databaseUrl
with your own details from Firebase.
Where to get those?
- Project ID: Firebase console > Your project (that you created earlier) > Project settings.
- Bucket Name: Firebase console > Your project > Build > Storage
- Database URL: Firebase console > Your project > Build > Realtime Database > Create Database
Then, your database URL will be shown at the top.
4. Create a Service
Create a FileStorageService
class with a constructor.
@Service
public class FileStorageService {
private final Storage storage;
private final FileMetaDataRepository repo;
private final String bucketName = "fir-fileuploadapi.appspot.com";
public FileStorageService(FileMetaDataRepository repo) throws IOException {
this.repo = repo;
ClassPathResource resource = new ClassPathResource("serviceAccountKey.json");
InputStream serviceAccount = resource.getInputStream();
GoogleCredentials credentials = GoogleCredentials.fromStream(serviceAccount)
.createScoped(Lists.newArrayList("https://www.googleapis.com/auth/cloud-platform"));
this.storage = StorageOptions.newBuilder().setCredentials(credentials).build().getService();
}
}
Here, just replace serviceAccountKey.json
with the name of the JSON file stored in the src/main/resources
folder and the Bucket Name
you got earlier.
Wait! What is FileMetaDataRepository
? We haven't created that yet.
Here you go!
@Repository
public interface FileMetaDataRepository extends JpaRepository<FileMetaData, Integer> {
FileMetaData findByUniqueId(String uniqueId);
}
And the corresponding FileMetaData.java
:
@Entity
@Table(name = "files")
public class FileMetaData {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@Column(name = "unique_id", nullable = false)
private String uniqueId;
@Column(name = "object_name", nullable = false)
private String objectName;
@Column(name = "upload_date", nullable = false)
private LocalDateTime uploadDate;
public FileMetaData() {}
public FileMetaData(Integer id, String uniqueId, String objectName, LocalDateTime uploadDate) {
this.id = id;
this.uniqueId = uniqueId;
this.objectName = objectName;
this.uploadDate = uploadDate;
}
// getters and setters go here...
}
What’s the need for this?
FileMetaData
is an object that holds metadata (data about the data) about the file we are storing. It's just for making our retrieval smoother!
5. Create the Uploading and Retrieving Methods
Uploading Files
In the FileStorageService
, we’ll create a method for uploading files:
public String uploadFile(MultipartFile file) throws IOException {
if (file.isEmpty()) {
throw new IllegalArgumentException("File is empty. Please upload a valid file.");
}
String uniqueID = UUID.randomUUID().toString();
String objectName = uniqueID + "_" + file.getOriginalFilename();
BlobId blobId = BlobId.of(bucketName, objectName);
BlobInfo blobInfo = BlobInfo.newBuilder(blobId).build();
storage.create(blobInfo, file.getBytes());
FileMetaData metaData = new FileMetaData();
metaData.setUniqueId(uniqueID);
metaData.setObjectName(objectName);
metaData.setUploadDate(LocalDateTime.now());
repo.save(metaData);
return uniqueID;
}
Explanation:
- We first check if the uploaded file is empty. If it is, we throw an exception.
- We generate a unique ID for the file using
UUID
to ensure no two files have the same identifier. - We create a
BlobId
andBlobInfo
for the file, then store the file in Firebase using thestorage.create()
method. - Finally, we save the file metadata in our repository for easy retrieval later.
Retrieving Files
Now, let’s create a method to retrieve files by their unique ID:
public FileResponse retrieveFile(String fileId) {
FileMetaData fileMetadata = repo.findByUniqueId(fileId);
if (fileMetadata == null) {
throw new IllegalArgumentException("No file found with the given ID: " + fileId);
}
String objectName = fileMetadata.getObjectName();
BlobId blobId = BlobId.of(bucketName, objectName);
Blob blob = storage.get(blobId);
if (blob == null || !blob.exists()) {
throw new IllegalArgumentException("No file found with the given ID: " + fileId);
}
FileResponse fileResponse = new FileResponse(objectName, blob.getContent());
return fileResponse;
}
Explanation:
- We look up the file metadata by its unique ID. If it doesn't exist, we throw an exception.
- We retrieve the
Blob
from Firebase using the object name stored in our metadata. - If the blob exists, we create a
FileResponse
object that contains the file name and its content, ready to be returned.
Here is how the FileResponse
class looks like,
public class FileResponse {
private String fileName;
private byte[] fileContent;
public FileResponse(String fileName, byte[] fileContent) {
this.fileName = fileName;
this.fileContent = fileContent;
}
public String getFileName() {
return fileName;
}
public byte[] getFileContent() {
return fileContent;
}
}
6. Create a Controller
Next, we’ll set up a controller to handle our HTTP requests:
@RestController
@RequestMapping("/api/files")
public class FileStorageController {
@Autowired
private FileStorageService fileStorageService;
@PostMapping("/upload")
public ResponseEntity<String> uploadFile(@RequestParam MultipartFile file) {
try {
String fileId = fileStorageService.uploadFile(file
);
return ResponseEntity.ok(fileId);
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(e.getMessage());
}
}
@GetMapping("/retrieve/{fileId}")
public ResponseEntity<FileResponse> retrieveFile(@PathVariable String fileId) {
try {
FileResponse fileResponse = fileStorageService.retrieveFile(fileId);
return ResponseEntity.ok(fileResponse);
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(null);
}
}
}
Explanation:
- The
uploadFile
method handles file uploads by calling our service method and returning the unique ID. - The
retrieveFile
method fetches the file using the unique ID and returns its content.
Testing the API 🧪
Now that everything is set up, you can test your API using Postman or any other tool.
-
To Upload a File:
- Make a
POST
request tohttp://localhost:8080/api/files/upload
with the file in the form-data.
- Make a
-
To Retrieve a File:
- Make a
GET
request tohttp://localhost:8080/api/files/retrieve/{fileId}
using the unique ID you received from the upload response.
- Make a
Conclusion
And there you have it! You’ve successfully created a Spring Boot REST API to upload and retrieve files using Firebase. I hope this guide was helpful and easy to follow! If you have any questions or need further assistance, feel free to drop a comment below!
Happy coding! 💻
Top comments (0)