Skip to main content
The File Uploads API provides endpoints for uploading various types of files to your portfolio. All files are stored in Google Drive and are publicly accessible through generated URLs.

Overview

All upload endpoints:
  • Require authentication via JWT token
  • Accept multipart/form-data content type
  • Upload files to dedicated Google Drive folders
  • Return public URLs for uploaded files
  • Generate unique filenames to prevent conflicts
All upload endpoints are under the /api/me/upload base path and require valid JWT authentication.

Google Drive Integration

Portfolio Hub uses Google Drive as the file storage backend with OAuth 2.0 authentication. Files are organized into separate folders:
  • User Avatars: Profile pictures
  • User Resumes: PDF/DOC resume files
  • Project Covers: Project cover images
  • Skill Icons: Skill/technology icons
  • Certificates: Certificate documents and images
Each uploaded file is automatically made publicly readable and accessible via a Google Drive URL in the format:
https://drive.google.com/uc?export=view&id={fileId}

File Size Limits

Spring Boot’s default multipart file upload limits apply:
  • Max file size: 1MB (default)
  • Max request size: 10MB (default)
These limits can be configured in the application properties using spring.servlet.multipart.max-file-size and spring.servlet.multipart.max-request-size.

Accepted File Formats

Avatar Uploads

  • Recommended: JPG, PNG, WebP
  • Best size: 400x400px or higher (square aspect ratio)

Resume Uploads

  • Recommended: PDF, DOC, DOCX
  • Note: PDF is preferred for consistent rendering

Project Cover Images

  • Recommended: JPG, PNG, WebP
  • Best size: 1200x630px (16:9 aspect ratio)

Skill Icons

  • Recommended: PNG, SVG, WebP
  • Best size: 64x64px or 128x128px (square)

Certificate Files

  • Recommended: PDF, JPG, PNG
  • Note: PDF for documents, images for scanned certificates

Upload Avatar

Request

file
file
required
The avatar image file to upload. Should be an image file (JPG, PNG, WebP).

Request Example

curl -X POST https://api.portfoliohub.com/api/me/upload/avatar \
  -H "Authorization: Bearer YOUR_JWT_TOKEN" \
  -F "[email protected]"

Response

success
boolean
Indicates if the upload was successful
message
string
Success message (e.g., “Avatar actualizado exitosamente”)
data
object
The updated profile data
data.id
number
Profile ID
data.slug
string
Profile slug/username
data.fullName
string
User’s full name
data.headline
string
Profile headline
data.bio
string
Profile bio/description
data.contactEmail
string
Contact email address
data.location
string
User’s location
data.avatarUrl
string
The newly uploaded avatar URL (Google Drive public URL)
data.resumeUrl
string
Resume file URL (if previously uploaded)
timestamp
string
ISO 8601 timestamp of the response

Response Example

{
  "success": true,
  "message": "Avatar actualizado exitosamente",
  "data": {
    "id": 1,
    "slug": "john-doe",
    "fullName": "John Doe",
    "headline": "Full Stack Developer",
    "bio": "Passionate developer with 5 years of experience",
    "contactEmail": "[email protected]",
    "location": "San Francisco, CA",
    "avatarUrl": "https://drive.google.com/uc?export=view&id=1a2b3c4d5e6f",
    "resumeUrl": "https://drive.google.com/uc?export=view&id=7g8h9i0j1k2l"
  },
  "timestamp": "2026-03-09T10:30:00Z"
}

Error Response

{
  "success": false,
  "message": "Error al subir avatar: Invalid file format",
  "data": null,
  "timestamp": "2026-03-09T10:30:00Z"
}

Upload Resume

Request

file
file
required
The resume file to upload. Recommended formats: PDF, DOC, DOCX.

Request Example

curl -X POST https://api.portfoliohub.com/api/me/upload/resume \
  -H "Authorization: Bearer YOUR_JWT_TOKEN" \
  -F "[email protected]"

Response

Returns the same structure as the avatar upload endpoint, with resumeUrl field updated to the newly uploaded file’s public URL.
{
  "success": true,
  "message": "Currículum actualizado exitosamente",
  "data": {
    "id": 1,
    "slug": "john-doe",
    "fullName": "John Doe",
    "headline": "Full Stack Developer",
    "bio": "Passionate developer with 5 years of experience",
    "contactEmail": "[email protected]",
    "location": "San Francisco, CA",
    "avatarUrl": "https://drive.google.com/uc?export=view&id=1a2b3c4d5e6f",
    "resumeUrl": "https://drive.google.com/uc?export=view&id=9m8n7o6p5q4r"
  },
  "timestamp": "2026-03-09T10:35:00Z"
}

Upload Project Cover

Path Parameters

projectId
number
required
The ID of the project to upload the cover image for

Request

file
file
required
The cover image file to upload. Recommended formats: JPG, PNG, WebP.

Request Example

curl -X POST https://api.portfoliohub.com/api/me/upload/project/5/cover \
  -H "Authorization: Bearer YOUR_JWT_TOKEN" \
  -F "[email protected]"

Response

success
boolean
Indicates if the upload was successful
message
string
Success message (e.g., “Portada de proyecto actualizada”)
data
object
The updated project data
data.id
number
Project ID
data.title
string
Project title
data.slug
string
Project slug/identifier
data.summary
string
Brief project summary
data.description
string
Detailed project description
data.repoUrl
string
Repository URL (e.g., GitHub)
data.liveUrl
string
Live demo URL
data.coverImage
string
The newly uploaded cover image URL (Google Drive public URL)
data.startDate
string
Project start date (ISO 8601)
data.endDate
string
Project end date (ISO 8601, null if ongoing)
Whether the project is featured
data.sortOrder
number
Display order
data.skills
array
Array of skills/technologies used in the project
timestamp
string
ISO 8601 timestamp of the response

Response Example

{
  "success": true,
  "message": "Portada de proyecto actualizada",
  "data": {
    "id": 5,
    "title": "E-Commerce Platform",
    "slug": "ecommerce-platform",
    "summary": "A modern e-commerce solution",
    "description": "Full-featured online store with payment integration",
    "repoUrl": "https://github.com/johndoe/ecommerce",
    "liveUrl": "https://shop.example.com",
    "coverImage": "https://drive.google.com/uc?export=view&id=3s4t5u6v7w8x",
    "startDate": "2025-01-15",
    "endDate": "2025-06-30",
    "featured": true,
    "sortOrder": 1,
    "skills": [
      {
        "id": 10,
        "name": "React",
        "globalSkillId": 1,
        "level": 5,
        "icon": "https://drive.google.com/uc?export=view&id=abc123"
      }
    ]
  },
  "timestamp": "2026-03-09T10:40:00Z"
}

Upload Skill Icon

Path Parameters

skillId
number
required
The ID of the skill to upload the icon for

Request

file
file
required
The icon file to upload. Recommended formats: PNG, SVG, WebP.

Request Example

curl -X POST https://api.portfoliohub.com/api/me/upload/skill/12/icon \
  -H "Authorization: Bearer YOUR_JWT_TOKEN" \
  -F "[email protected]"

Response

success
boolean
Indicates if the upload was successful
message
string
Success message (e.g., “Icono de skill actualizado”)
data
object
The updated skill data
data.id
number
Skill ID
data.name
string
Skill name (e.g., “React”, “Python”)
data.globalSkillId
number
Global skill identifier (if linked to a predefined skill)
data.level
number
Proficiency level (1-5)
data.icon
string
The newly uploaded icon URL (Google Drive public URL)
timestamp
string
ISO 8601 timestamp of the response

Response Example

{
  "success": true,
  "message": "Icono de skill actualizado",
  "data": {
    "id": 12,
    "name": "React",
    "globalSkillId": 1,
    "level": 5,
    "icon": "https://drive.google.com/uc?export=view&id=9y8x7w6v5u4t"
  },
  "timestamp": "2026-03-09T10:45:00Z"
}

Upload Certificate File

Path Parameters

certificateId
number
required
The ID of the certificate to upload the file for

Request

file
file
required
The certificate file to upload. Recommended formats: PDF, JPG, PNG.

Request Example

curl -X POST https://api.portfoliohub.com/api/me/upload/certificate/8/file \
  -H "Authorization: Bearer YOUR_JWT_TOKEN" \
  -F "[email protected]"

Response

success
boolean
Indicates if the upload was successful
message
string
Success message (e.g., “Archivo de certificado subido”)
data
object
The updated certificate data
data.id
number
Certificate ID
data.educationId
number
Associated education/course ID
data.name
string
Certificate name/title
data.description
string
Certificate description
data.imageUrl
string
The newly uploaded certificate file URL (Google Drive public URL)
data.fileId
string
Google Drive file ID for the uploaded certificate
timestamp
string
ISO 8601 timestamp of the response

Response Example

{
  "success": true,
  "message": "Archivo de certificado subido",
  "data": {
    "id": 8,
    "educationId": 3,
    "name": "AWS Certified Solutions Architect",
    "description": "Professional certification for AWS cloud architecture",
    "imageUrl": "https://drive.google.com/uc?export=view&id=4t3r2e1w0q9p",
    "fileId": "4t3r2e1w0q9p"
  },
  "timestamp": "2026-03-09T10:50:00Z"
}

Error Handling

All upload endpoints may return the following error responses:

401 Unauthorized

{
  "success": false,
  "message": "Authentication required",
  "data": null,
  "timestamp": "2026-03-09T10:55:00Z"
}

404 Not Found

Returned when trying to upload to a non-existent resource (e.g., invalid projectId, skillId, or certificateId).
{
  "success": false,
  "message": "Project not found with id: 999",
  "data": null,
  "timestamp": "2026-03-09T10:55:00Z"
}

500 Internal Server Error

Returned when file upload to Google Drive fails or other server errors occur.
{
  "success": false,
  "message": "Error al subir archivo: IOException occurred during upload",
  "data": null,
  "timestamp": "2026-03-09T10:55:00Z"
}
All error responses include descriptive error messages in the message field to help diagnose issues.

File Naming Convention

Uploaded files are automatically renamed using the following pattern:
{entity-identifier}_{type}_{timestamp}.{extension}
Examples:
  • Avatar: john-doe_avatar_1709985000000.jpg
  • Resume: john-doe_resume_1709985000000.pdf
  • Project cover: ecommerce-platform_cover_1709985000000.png
  • Skill icon: react_icon_1709985000000.svg
  • Certificate: aws-certified_certificate_1709985000000.pdf
This naming convention ensures:
  • Uniqueness: Timestamp prevents naming conflicts
  • Traceability: Entity identifier shows what the file belongs to
  • Organization: Type indicates the file’s purpose

Security Considerations

Authentication Required: All upload endpoints require a valid JWT token in the Authorization header.
  • File Validation: While basic validation occurs, always validate file types on the client side
  • Size Limits: Respect the configured file size limits to prevent timeouts
  • Public Access: All uploaded files are publicly accessible via Google Drive URLs
  • Ownership: Users can only upload files to their own resources (enforced by authentication)
  • Path Parameters: The API validates that resources (projects, skills, certificates) belong to the authenticated user

Best Practices

  1. Image Optimization: Compress images before uploading to reduce file size and improve load times
  2. File Format Selection: Use appropriate formats (PNG for transparency, JPG for photos, PDF for documents)
  3. Error Handling: Implement proper error handling for upload failures and retry logic
  4. Progress Tracking: For large files, implement upload progress indicators in your UI
  5. Validation: Validate file types and sizes on the client side before uploading
  6. Accessibility: Provide alt text and descriptions for uploaded images

Build docs developers (and LLMs) love