AngularJS实现Blob文件编辑上传及Spring Boot文件存储方案咨询
Hey, let's work through this together! The main problem right now is that your backend isn't returning key metadata for each file (like content type, filename, and a unique identifier). Without that, your frontend can't create proper Blobs for preview, and your backend won't know which existing file to replace when you upload updates. Here's a full, actionable solution:
一、First: Adjust Your Spring Boot Backend
First, you need to modify your backend to return file data along with metadata, not just raw byte arrays. Let's create a simple DTO to wrap this info:
public class FileDto { private Long id; // Unique ID from your DB to identify which file to update private String fileName; private String contentType; private byte[] content; // Add constructors, getters, and setters here }
Update your /landownerDetails/{email} endpoint to return a List<FileDto> instead of a raw byte array. Now your frontend will get all the info it needs to handle each file correctly.
For the update endpoint (/user/landownerDetails/editProfile/{email}), set it up to accept:
- The user's basic profile details
- Updated files (either replacing existing ones via their ID, or new files)
You can use MultipartFile to handle uploaded files, and pass along the file IDs to map new files to existing database records. Here's a simplified example of the update controller:
@PostMapping("/user/landownerDetails/editProfile/{email}") public ResponseEntity<?> updateLandOwnerProfile( @PathVariable String email, @RequestParam String fname, @RequestParam String lname, // Add all your other user profile fields here @RequestParam(value = "files", required = false) MultipartFile[] files, @RequestParam(value = "fileIds", required = false) Long[] fileIds) { // 1. Update the user's basic profile details first LandOwner landOwner = landOwnerRepository.findByEmail(email).orElseThrow(); landOwner.setFname(fname); landOwner.setLname(lname); // Set other user fields... landOwnerRepository.save(landOwner); // 2. Handle file updates if (files != null && fileIds != null && files.length == fileIds.length) { for (int i = 0; i < files.length; i++) { MultipartFile uploadedFile = files[i]; Long fileId = fileIds[i]; if (fileId != null) { // Replace an existing file: find the record by ID and update it UserDocument existingDoc = userDocumentRepository.findById(fileId).orElseThrow(); existingDoc.setContent(uploadedFile.getBytes()); existingDoc.setFileName(uploadedFile.getOriginalFilename()); existingDoc.setContentType(uploadedFile.getContentType()); userDocumentRepository.save(existingDoc); } else { // Add a new file: create a new record linked to the user UserDocument newDoc = new UserDocument(); newDoc.setContent(uploadedFile.getBytes()); newDoc.setFileName(uploadedFile.getOriginalFilename()); newDoc.setContentType(uploadedFile.getContentType()); newDoc.setLandOwner(landOwner); userDocumentRepository.save(newDoc); } } } return ResponseEntity.ok(Map.of("status", true)); }
二、Now: Fix Your AngularJS Frontend
1. Process the Backend Response & Prepare Files for Display
In your landOwnerEditProfile controller, when you receive the userdocuments array, convert each entry to a usable object that includes the Blob, metadata, and a spot for new files:
serviceCalls.landOwnerEditProfile(sessionStorage.emailId) .then((response)=>{ console.log(response.data); let status = response.status; if(status===200){ let landownerDetails = response.data; let landowner = landownerDetails.dayleasingusers; let userdocuments = landownerDetails.userdocuments; // Map the backend FileDto array to frontend-friendly objects $scope.files = userdocuments.map(doc => { // Create a Blob using the contentType from the backend const fileBlob = new Blob([doc.content], { type: doc.contentType }); return { id: doc.id, // Keep the DB ID to identify which file to update fileName: doc.fileName, contentType: doc.contentType, blob: fileBlob, newFile: null // Will hold the user's selected replacement file }; }); // ... Your existing code to populate user profile fields ... } })
2. Display Files & Add Edit/Replace Options
Add this HTML to render each file and let users replace them:
<div ng-repeat="file in files" class="file-item"> <h4>{{file.fileName}}</h4> <!-- Preview based on file type --> <div class="file-preview"> <img ng-if="file.contentType.startsWith('image/')" ng-src="{{getBlobUrl(file.blob)}}" alt="{{file.fileName}}" style="max-width: 150px; max-height: 150px;"> <object ng-if="file.contentType === 'application/pdf'" data="{{getBlobUrl(file.blob)}}" type="application/pdf" style="width: 200px; height: 250px;"> Can't preview PDF - <a ng-href="{{getBlobUrl(file.blob)}}" download="{{file.fileName}}">Download</a> </object> <p ng-if="!file.contentType.startsWith('image/') && file.contentType !== 'application/pdf'"> {{file.contentType}} file - <a ng-href="{{getBlobUrl(file.blob)}}" download="{{file.fileName}}">Download</a> </p> </div> <!-- Replace file input --> <input type="file" ng-change="replaceFile(file, $event)" class="mt-2" <!-- Remove accept if you want to allow changing file types --> accept="{{file.contentType}}"> </div>
Add these helper functions to your controller to handle Blob URLs and file replacement:
// Create a temporary URL for the Blob to render/ download $scope.getBlobUrl = function(blob) { return URL.createObjectURL(blob); }; // Handle when a user selects a new file to replace an existing one $scope.replaceFile = function(file, event) { const selectedFile = event.target.files[0]; if (selectedFile) { file.newFile = selectedFile; // Update the preview Blob with the new file file.blob = new Blob([selectedFile], { type: selectedFile.type }); } }; // Clean up Blob URLs when the controller is destroyed to avoid memory leaks $scope.$on('$destroy', function() { $scope.files.forEach(file => { if (file.blob) { URL.revokeObjectURL(file.blob); } }); });
3. Update Your Upload Service to Send Modified Files
Modify your updateLandOwnerProfile service method to only send files that the user has modified, along with their IDs so the backend knows which records to update:
this.updateLandOwnerProfile = function(details, email) { var differed = $q.defer(); $rootScope.config.headers.Authorization = sessionStorage.Authorization; var formdata = new FormData(); // Add all user profile fields to FormData formdata.append("email", email); formdata.append("fname", details.user.fname); formdata.append("lname", details.user.lname); formdata.append("city", details.user.city); formdata.append("phone1", details.user.phone1); formdata.append("phone2", details.user.phone2); formdata.append("phone3", details.user.phone3); formdata.append("streetAddressLine1", details.user.streetAddressLine1); formdata.append("streetAddressLine2", details.user.streetAddressLine2); formdata.append("stripeApiKey", details.user.stripeApiKey); formdata.append("zipcode", details.user.zipcode); formdata.append("businessName", details.user.businessName); // Add only modified files (where newFile is not null) const modifiedFiles = details.files.filter(file => file.newFile !== null); modifiedFiles.forEach(file => { formdata.append("files", file.newFile); formdata.append("fileIds", file.id); // Send the original file ID }); $http.post($rootScope.URL + "/user/landownerDetails/editProfile/"+email+"/", formdata, { transformRequest: angular.identity, headers: { "Content-Type": undefined, "Authorization": $rootScope.config.headers.Authorization } }) .then(function(response) { if(!response.data.status){ throw new Error("Details Not Updated"); } console.log("Details updated success"); differed.resolve(response); }) .catch(function(response) { console.log("Update failed", response); differed.reject(response); }); return differed.promise; }
Key Things to Remember
- Metadata is critical: Without sending the file ID, contentType, and filename between frontend and backend, you can't properly handle file previews or updates.
- Clean up Blob URLs: Always revoke temporary Blob URLs when you're done with them to prevent memory leaks in the browser.
- Only send changed files: No need to re-upload every file every time — just send the ones the user modified to save bandwidth.
- Flexible file types: If you want to let users change the file type (e.g., replace a BMP with a PDF), just remove the
acceptattribute from the file input.
内容的提问来源于stack exchange,提问作者Sm Srikanth

