Komplett guide för att ladda upp filer till Blue med hjälp av GraphQL-mutationer eller REST API
Översikt
Denna guide visar hur man laddar upp filer till Blue med två olika metoder:
- Direkt GraphQL-uppladdning (Rekommenderas) - Enkel uppladdning i ett steg med en filstorleksgräns på 256 MB
- REST API-uppladdning - Tre-stegsprocess som stöder större filer upp till 4,8 GB
Här är en jämförelse av de två metoderna:
Funktion | GraphQL-uppladdning | REST API-uppladdning |
---|---|---|
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 |
GraphQL-filuppladdning
GraphQL-uppladdningsmetoden erbjuder ett enkelt, direkt sätt att ladda upp filer med en enda begäran.
uploadFile
Laddar upp en enda fil till filsystemet och skapar en filreferens i databasen.
Inmatning:
file: Upload!
- Filen som ska laddas upp (använder multipart/form-data)projectId: String!
- Projekt-ID eller slug där filen kommer att lagrascompanyId: String!
- Företags-ID eller slug där filen kommer att lagras
Returnerar: File!
- Det skapade filobjektet
Exempel:
mutation UploadFile($input: UploadFileInput!) {
uploadFile(input: $input) {
id
uid
name
size
type
extension
shared
createdAt
project {
id
name
}
folder {
id
title
}
}
}
uploadFiles
Laddar upp flera filer till filsystemet och skapar filreferenser i databasen.
Inmatning:
files: [Upload!]!
- Array av filer som ska laddas upp (max 10)projectId: String!
- Projekt-ID eller slug där filerna kommer att lagrascompanyId: String!
- Företags-ID eller slug där filerna kommer att lagras
Returnerar: [File!]!
- Array av skapade filobjekt
Exempel:
mutation UploadFiles($input: UploadFilesInput!) {
uploadFiles(input: $input) {
id
uid
name
size
type
extension
shared
createdAt
}
}
Klientimplementering
Apollo Client (JavaScript)
Uppladdning av en fil:
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##80c6736a-480d-4dd6-8c24-85e30675db5d##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"
}
}
});
Vanilla JavaScript
Single File Upload:
<!-- 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');
}
}
@@CB##ef54eb27-3fa1-45ff-be08-9fd612004305##CB@@html
<!-- HTML -->
<input type="file" id="filesInput" multiple />
<button onclick="uploadFiles()">Upload Files</button>
@@CB##96a97438-511a-4de9-bbae-33a27e3a3129##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} filer laddades upp framgångsrikt!`);
}
} 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##b55fb166-9a9f-4d0f-b7ae-96e0c766c366##CB@@
This is diagram that shows the flow of the upload process:
Uploading to File Tab
::code-group
@@CB##bf8ad5c5-5a45-47e5-84a1-9fca2d67cd9a##CB@@
::
Steps Explained
Step 1: Request Upload Credentials
- Send a GET request to
https://api.blue.cc/uploads?filename=test.jpg@@CODE##449a8aae-c46b-4125-83c3-5a4fa1ca99bb##CODE@@files@@CODE##bc982042-5c13-4850-8747-53305334a7f8##CODE@@requests.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@@CODE##876f0cd3-e943-42b7-ac2c-e99dfc3b56a1##CODE@@os.path.getsize@@CODE##bfd1a424-8bb3-4ded-b51a-2791887bb6a8##CODE@@todoId@@CODE##6c19ffd5-9870-46df-8668-a120390497bc##CODE@@customFieldId@@CODE##341fb30d-4ab0-4212-9a72-4d0ed7b54490##CODE@@``json
{
"data": {
"createTodoCustomFieldFile": true
}
}