Guide complet pour télécharger des fichiers vers Blue en utilisant des mutations GraphQL ou l'API REST
Vue d'ensemble
Ce guide démontre comment télécharger des fichiers vers Blue en utilisant deux approches différentes :
- Téléchargement GraphQL direct (Recommandé) - Téléchargement simple en une étape avec une limite de taille de fichier de 256 Mo
- Téléchargement API REST - Processus en trois étapes prenant en charge des fichiers plus volumineux jusqu'à 4,8 Go
Voici une comparaison des deux méthodes :
Fonctionnalité | Téléchargement GraphQL | Téléchargement API REST |
---|---|---|
Complexity | Simple (one request) | Complex (three steps) |
File Size Limit | 256MB per file | 4.8GB per file |
Batch Upload | Up to 10 files | Single file only |
Implementation | Direct mutation | Multi-step process |
Best For | Most use cases | Large files only |
Téléchargement de fichiers GraphQL
La méthode de téléchargement GraphQL fournit un moyen simple et direct de télécharger des fichiers avec une seule requête.
uploadFile
Télécharge un fichier unique vers le système de stockage de fichiers et crée une référence de fichier dans la base de données.
Entrée :
file: Upload!
- Le fichier à télécharger (en utilisant multipart/form-data)projectId: String!
- ID ou slug du projet où le fichier sera stockécompanyId: String!
- ID ou slug de l'entreprise où le fichier sera stocké
Retourne : File!
- L'objet fichier créé
Exemple :
mutation UploadFile($input: UploadFileInput!) {
uploadFile(input: $input) {
id
uid
name
size
type
extension
shared
createdAt
project {
id
name
}
folder {
id
title
}
}
}
uploadFiles
Télécharge plusieurs fichiers vers le système de stockage de fichiers et crée des références de fichiers dans la base de données.
Entrée :
files: [Upload!]!
- Tableau de fichiers à télécharger (max 10)projectId: String!
- ID ou slug du projet où les fichiers seront stockéscompanyId: String!
- ID ou slug de l'entreprise où les fichiers seront stockés
Retourne : [File!]!
- Tableau des objets fichiers créés
Exemple :
mutation UploadFiles($input: UploadFilesInput!) {
uploadFiles(input: $input) {
id
uid
name
size
type
extension
shared
createdAt
}
}
Implémentation du client
Client Apollo (JavaScript)
Téléchargement d'un fichier unique :
import { gql } from '@apollo/client';
const UPLOAD_FILE = gql`
mutation UploadFile($input: UploadFileInput!) {
uploadFile(input: $input) {
id
uid
name
size
type
extension
shared
createdAt
}
}
`;
// Using file input
const fileInput = document.querySelector('input[type="file"]');
const file = fileInput.files[0];
const { data } = await uploadFile({
variables: {
input: {
file: file,
projectId: "project_123", // or "my-project-slug"
companyId: "company_456" // or "my-company-slug"
}
}
});
@@CB##1ac502c3-0ccb-4c1e-9d00-77c2596253ec##CB@@javascript
const UPLOAD_FILES = gql`
mutation UploadFiles($input: UploadFilesInput!) {
uploadFiles(input: $input) {
id
uid
name
size
type
extension
shared
createdAt
}
}
`;
// Using multiple file inputs
const fileInputs = document.querySelectorAll('input[type="file"]');
const files = Array.from(fileInputs).map(input => input.files[0]).filter(Boolean);
const { data } = await uploadFiles({
variables: {
input: {
files: files,
projectId: "project_123", // or "my-project-slug"
companyId: "company_456" // or "my-company-slug"
}
}
});
@@CB##2f03a82f-4020-4e67-bc40-0c7e8e6b265f##CB@@html
<!-- HTML -->
<input type="file" id="fileInput" />
<button onclick="uploadFile()">Upload File</button>
async function uploadFile() {
const fileInput = document.getElementById('fileInput');
const file = fileInput.files[0];
if (!file) {
alert('Please select a file');
return;
}
// Create GraphQL mutation
const query = `
mutation UploadFile($input: UploadFileInput!) {
uploadFile(input: $input) {
id
name
size
type
extension
createdAt
}
}
`;
// Prepare form data
const formData = new FormData();
formData.append('operations', JSON.stringify({
query: query,
variables: {
input: {
file: null, // Will be replaced by file
projectId: "your_project_id", // or "your-project-slug"
companyId: "your_company_id" // or "your-company-slug"
}
}
}));
formData.append('map', JSON.stringify({
"0": ["variables.input.file"]
}));
formData.append('0', file);
try {
const response = await fetch('/graphql', {
method: 'POST',
body: formData,
headers: {
// Don't set Content-Type - let browser set it with boundary
'Authorization': 'Bearer your_auth_token'
}
});
const result = await response.json();
if (result.errors) {
console.error('Upload failed:', result.errors);
alert('Upload failed: ' + result.errors[0].message);
} else {
console.log('Upload successful:', result.data.uploadFile);
alert('File uploaded successfully!');
}
} catch (error) {
console.error('Network error:', error);
alert('Network error during upload');
}
}
Multiple Files Upload:
<!-- HTML -->
<input type="file" id="filesInput" multiple />
<button onclick="uploadFiles()">Upload Files</button>
@@CB##b5843709-9074-415f-ba09-177c2007ff4f##CB@@javascript
async function uploadFiles() {
const filesInput = document.getElementById('filesInput');
const files = Array.from(filesInput.files);
if (files.length === 0) {
alert('Please select files');
return;
}
if (files.length > 10) {
alert('Maximum 10 files allowed');
return;
}
const query = `
mutation UploadFiles($input: UploadFilesInput!) {
uploadFiles(input: $input) {
id
name
size
type
extension
createdAt
}
}
`;
const formData = new FormData();
// Create file placeholders for variables
const fileVariables = files.map((_, index) => null);
formData.append('operations', JSON.stringify({
query: query,
variables: {
input: {
files: fileVariables,
projectId: "your_project_id", // or "your-project-slug"
companyId: "your_company_id" // or "your-company-slug"
}
}
}));
// Create map for file replacements
const map = {};
files.forEach((_, index) => {
map[index.toString()] = [`variables.input.files.${index}`];
});
formData.append('map', JSON.stringify(map));
// Append actual files
files.forEach((file, index) => {
formData.append(index.toString(), file);
});
try {
const response = await fetch('/graphql', {
method: 'POST',
body: formData,
headers: {
'Authorization': 'Bearer your_auth_token'
}
});
const result = await response.json();
if (result.errors) {
console.error('Upload failed:', result.errors);
alert('Upload failed: ' + result.errors[0].message);
} else {
console.log('Upload successful:', result.data.uploadFiles);
alert(`${result.data.uploadFiles.length} fichiers téléchargés avec succès !`);
}
} catch (error) {
console.error('Network error:', error);
alert('Network error during upload');
}
}
cURL Example
# Single file upload with cURL
curl -X POST \
-H "Authorization: Bearer your_auth_token" \
-F 'operations={"query":"mutation UploadFile($input: UploadFileInput!) { uploadFile(input: $input) { id name size type extension createdAt } }","variables":{"input":{"file":null,"projectId":"your_project_id","companyId":"your_company_id"}}}' \
-F 'map={"0":["variables.input.file"]}' \
-F '0=@/path/to/your/file.jpg' \
https://your-api.com/graphql
REST API Upload
Use this method for files larger than 256MB (up to 4.8GB). This approach uses a three-step process: request upload credentials, upload to storage, then register the file in the database.
Prerequisites:
- Python 3.x installed
requests
library installed:pip install requests
- A valid X-Bloo-Token-ID and X-Bloo-Token-Secret for Blue API authentication
- The file to upload (e.g., test.jpg) in the same directory as the script
This method covers two scenarios:
- Uploading to the "File Tab"
- Uploading to the "Todo File Custom Field"
Configuration
Define these constants at the top of your script:
@@CB##31cc9a45-4b33-435e-af3c-8c6cef69e4d3##CB@@
This is diagram that shows the flow of the upload process:
Uploading to File Tab
::code-group
@@CB##e1bf7351-0aba-427f-8c93-92785f38e101##CB@@
::
Steps Explained
Step 1: Request Upload Credentials
- Send a GET request to
https://api.blue.cc/uploads?filename=test.jpg
- Returns S3 credentials in JSON format
Sample Response:
{
"url": "https://s3.ap-southeast-1.amazonaws.com/bloo-uploads",
"fields": {
"acl": "private",
"Content-Disposition": "attachment; filename=\"test.jpg\"",
"Key": "cm8qujq3b01p22lrv9xbb2q62/test.jpg",
"X-Key": "cm8qujq3b01p22lrv9xbb2q62",
"Content-Type": "image/jpeg",
"bucket": "bloo-uploads",
"X-Amz-Algorithm": "AWS4-HMAC-SHA256",
"X-Amz-Credential": "AKIA2LAMM6FPGT53RMQY/20250327/ap-southeast-1/s3/aws4_request",
"X-Amz-Date": "20250327T042138Z",
"Policy": "...",
"X-Amz-Signature": "e1d2446a0bdbfcbd3a73f926c7d92c87460f8d8ae030d717355f2790d6609452"
}
}
Step 2: Upload File to S3
- Uses the
files
parameter inrequests.post
to send a multipart/form-data request to the S3 URL - Ensures all fields match the policy exactly, avoiding curl's formatting issues
Step 3: Register File Metadata
- Sends a GraphQL mutation to
https://api.blue.cc/graphql
- Dynamically calculates file size with
os.path.getsize
Sample Response:
@@CB##bbd63a3c-6349-4966-aea7-6ee550097125##CB@@
Uploading to Custom Field
::code-group
@@CB##6337557e-d1dc-4d5b-91e9-80f2aac5093a##CB@@
::
Steps 1-3 are the same as the File Tab process (fetch credentials, upload to S3, and register file metadata).
For step 4, you need to associate the file with a todo custom field.
- Sends a GraphQL mutation to link the file to a todo custom field
- Uses
todoId
and `customFieldId@@CODE##530a6a83-bf09-407d-a922-ec125a68464c##CODE@@``json
{
"data": {
"createTodoCustomFieldFile": true
}
}