Vue + Spring Boot: Document Batch Upload Design





Document Batch Upload Conversation


Vue + Spring Boot: Document Batch Upload Design

🧩 Scenario

You are building a batch document upload feature with the following requirements:

  • Each file has its own metadata: originalFileName, title
  • The group of files shares metadata: description, tags, categoryId, accessLevel
  • When submitted, Spring Boot backend stores group metadata to upload_groups table and each file to documents table, along with related tables like document_tags, document_categories

🖼️ Vue.js Frontend

<script setup>
import { ref } from 'vue'
import axios from 'axios'

const files = ref([])
const fileMetadataList = ref([]) // [{ originalFileName, title }]

const groupMetadata = ref({
  description: '',
  tags: ['AI', 'Spring Boot'],
  categoryId: 1,
  accessLevel: 'PRIVATE'
})

const handleSubmit = async () => {
  const formData = new FormData()

  files.value.forEach((file) => {
    formData.append('files', file)
  })

  const payload = {
    documents: fileMetadataList.value,
    ...groupMetadata.value
  }

  formData.append('requestJson', new Blob([JSON.stringify(payload)], { type: 'application/json' }))

  await axios.post('/api/documents/batch-upload', formData, {
    headers: { 'Content-Type': 'multipart/form-data' }
  })
}
</script>

🧾 JSON structure sent as requestJson

{
  "description": "Batch upload of AI reports",
  "tags": ["AI", "Spring Boot"],
  "categoryId": 1,
  "accessLevel": "PRIVATE",
  "documents": [
    { "originalFileName": "doc1.pdf", "title": "AI Summary Report" },
    { "originalFileName": "doc2.pdf", "title": "Spring Boot Best Practices" }
  ]
}

🎯 Spring Boot Backend

📦 DTOs

public class DocumentMetadata {
    private String originalFileName;
    private String title;
    // Getters and setters
}

public class DocumentBatchUploadRequest {
    private String description;
    private List<String> tags;
    private Long categoryId;
    private String accessLevel;
    private List<DocumentMetadata> documents;
    // Getters and setters
}

🛠️ Controller

@PostMapping(value = "/batch-upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public ResponseEntity<?> uploadDocuments(
        @RequestPart("requestJson") DocumentBatchUploadRequest request,
        @RequestPart("files") List<MultipartFile> files
) {
    Long groupId = uploadGroupService.saveOrUpdate(
        request.getDescription(),
        request.getTags(),
        request.getCategoryId(),
        request.getAccessLevel()
    );

    for (int i = 0; i < files.size(); i++) {
        MultipartFile file = files.get(i);
        DocumentMetadata meta = request.getDocuments().get(i);

        documentService.saveOrUpdate(
            file,
            meta.getOriginalFileName(),
            meta.getTitle(),
            groupId,
            request.getTags(),
            request.getCategoryId(),
            request.getAccessLevel()
        );
    }

    return ResponseEntity.ok("Batch upload successful");
}

🧱 Database Design Suggestions

  • upload_groups: id, description, tags (JSON or tag join table), category_id, access_level
  • documents: id, group_id (FK), title, file_path, original_file_name, category_id, access_level
  • document_tags: document_id, tag
  • document_categories: optional join table or FK in documents

✅ Summary

  • Vue: Collect file and metadata, send as FormData
  • Spring Boot: Parse @RequestPart JSON and file parts
  • Database: Save group first, then each document linked to group

📌 Optional Enhancements

  • Progress bar UI with Axios upload progress
  • Asynchronous processing using Kafka or ExecutorService
  • Cloud file storage (S3, OSS, etc.)
  • Comprehensive validation and error handling


Leave a Comment

Your email address will not be published. Required fields are marked *