Test file upload functionality including images, documents, and other binary data with multipart form submissions.
Loading Files
Use the open() function to read file contents during the init context (before VU execution):
Text Files
Load and parse JSON data:
{
"my_key" : "has a value"
}
const data = JSON . parse ( open ( './data.json' ));
export default function () {
console . log ( data . my_key ); // Output: has a value
}
Binary Files
Add "b" as the second argument to open binary files:
const binFile = open ( './image.png' , 'b' );
export default function () {
// Use binFile for uploads
}
The open() function executes in the init context, so files are loaded once per VU before the test starts. This is more efficient than reading files in every iteration.
Basic File Upload
Upload a file using multipart form data:
import http from 'k6/http' ;
import { sleep } from 'k6' ;
const binFile = open ( '/path/to/file.bin' , 'b' );
export default function () {
const data = {
field: 'this is a standard form field' ,
file: http . file ( binFile , 'test.bin' ),
};
const res = http . post ( 'https://example.com/upload' , data );
sleep ( 3 );
}
When a JavaScript object passed to http.post() contains a FileData property (created by http.file()), k6 automatically constructs a multipart request.
Image Upload Example
Upload an image with metadata:
import http from 'k6/http' ;
import { check } from 'k6' ;
const image = open ( './profile-photo.jpg' , 'b' );
export default function () {
const data = {
username: 'testuser' ,
description: 'My profile photo' ,
photo: http . file ( image , 'profile.jpg' , 'image/jpeg' ),
};
const res = http . post ( 'https://api.example.com/upload/profile' , data );
check ( res , {
'upload successful' : ( r ) => r . status === 200 ,
'has file URL' : ( r ) => r . json ( 'url' ) !== undefined ,
});
}
Advanced Multipart Uploads
For more control over multipart requests, use the FormData polyfill:
Multiple File Upload
import http from 'k6/http' ;
import { check } from 'k6' ;
import { FormData } from 'https://jslib.k6.io/formdata/0.0.2/index.js' ;
const img1 = open ( '/path/to/image1.png' , 'b' );
const img2 = open ( '/path/to/image2.jpg' , 'b' );
const txt = open ( '/path/to/text.txt' );
export default function () {
const fd = new FormData ();
fd . append ( 'someTextField' , 'someValue' );
fd . append ( 'aBinaryFile' , {
data: new Uint8Array ( img1 ). buffer ,
filename: 'logo.png' ,
content_type: 'image/png' ,
});
fd . append ( 'anotherTextField' , 'anotherValue' );
fd . append ( 'images' , http . file ( img1 , 'image1.png' , 'image/png' ));
fd . append ( 'images' , http . file ( img2 , 'image2.jpg' , 'image/jpeg' ));
fd . append ( 'text' , http . file ( txt , 'text.txt' , 'text/plain' ));
const res = http . post ( 'https://quickpizza.grafana.com/api/post' , fd . body (), {
headers: { 'Content-Type' : 'multipart/form-data; boundary=' + fd . boundary },
});
check ( res , {
'is status 200' : ( r ) => r . status === 200 ,
});
}
When using FormData, you must manually set the Content-Type header with the boundary value.
The FormData polyfill solves two limitations:
Ordered fields : JavaScript objects don’t guarantee property order, but some APIs (like AWS S3) require specific field ordering
Multiple files per field : Upload multiple files under the same form field name
Basic Upload
FormData Upload
Simple uploads with automatic multipart handling: const data = {
file: http . file ( binFile , 'file.bin' ),
};
http . post ( url , data );
Advanced uploads with explicit control: const fd = new FormData ();
fd . append ( 'file1' , http . file ( binFile1 , 'file1.bin' ));
fd . append ( 'file2' , http . file ( binFile2 , 'file2.bin' ));
http . post ( url , fd . body (), {
headers: { 'Content-Type' : 'multipart/form-data; boundary=' + fd . boundary },
});
Upload with Authentication
Combine file uploads with authentication:
import http from 'k6/http' ;
import { check } from 'k6' ;
const document = open ( './report.pdf' , 'b' );
export function setup () {
// Get authentication token
const loginRes = http . post (
'https://api.example.com/auth/login' ,
JSON . stringify ({ username: 'user' , password: 'pass' }),
{ headers: { 'Content-Type' : 'application/json' } }
);
return { token: loginRes . json ( 'token' ) };
}
export default function ( data ) {
const uploadData = {
title: 'Monthly Report' ,
category: 'reports' ,
file: http . file ( document , 'report.pdf' , 'application/pdf' ),
};
const params = {
headers: {
'Authorization' : `Bearer ${ data . token } ` ,
},
};
const res = http . post ( 'https://api.example.com/upload' , uploadData , params );
check ( res , {
'upload successful' : ( r ) => r . status === 201 ,
'has document ID' : ( r ) => r . json ( 'id' ) !== undefined ,
});
}
Dynamic File Generation
Generate file content dynamically:
import http from 'k6/http' ;
import { randomString } from 'https://jslib.k6.io/k6-utils/1.2.0/index.js' ;
export default function () {
// Generate CSV content
const csvContent = `name,email,age \n ${ randomString ( 8 ) } ,[email protected] ,25 \n ` ;
const data = {
description: 'Generated CSV data' ,
file: http . file ( csvContent , 'data.csv' , 'text/csv' ),
};
http . post ( 'https://api.example.com/upload' , data );
}
Large File Uploads
Test large file uploads with progress tracking:
import http from 'k6/http' ;
import { Counter , Trend } from 'k6/metrics' ;
const uploadSize = new Trend ( 'upload_size_bytes' );
const uploadCounter = new Counter ( 'uploads_completed' );
const largeFile = open ( './large-video.mp4' , 'b' );
export const options = {
vus: 5 ,
duration: '1m' ,
thresholds: {
'http_req_duration' : [ 'p(95)<30000' ], // 95% of uploads under 30s
'upload_size_bytes' : [ 'avg>10000000' ], // Average upload over 10MB
},
};
export default function () {
const data = {
video: http . file ( largeFile , 'video.mp4' , 'video/mp4' ),
title: 'Test Video' ,
};
const res = http . post ( 'https://api.example.com/upload/video' , data );
if ( res . status === 200 ) {
uploadSize . add ( largeFile . length );
uploadCounter . add ( 1 );
}
}
Large file uploads consume memory. With multiple VUs, monitor system resources to avoid out-of-memory errors.
Common Scenarios
Avatar/Profile Picture Upload
import http from 'k6/http' ;
import { check } from 'k6' ;
const avatar = open ( './avatars/user1.png' , 'b' );
export default function () {
const data = {
userId: `user_ ${ __VU } ` ,
avatar: http . file ( avatar , 'avatar.png' , 'image/png' ),
};
const res = http . post ( 'https://api.example.com/users/avatar' , data );
check ( res , {
'avatar uploaded' : ( r ) => r . status === 200 ,
'returns avatar URL' : ( r ) => r . json ( 'avatarUrl' ) !== undefined ,
});
}
Document Upload with Metadata
import { FormData } from 'https://jslib.k6.io/formdata/0.0.2/index.js' ;
import http from 'k6/http' ;
const files = [
open ( './file1.txt' , 'b' ),
open ( './file2.txt' , 'b' ),
open ( './file3.txt' , 'b' ),
];
export default function () {
const fd = new FormData ();
files . forEach (( file , index ) => {
fd . append ( 'files' , http . file ( file , `file ${ index + 1 } .txt` , 'text/plain' ));
});
http . post ( 'https://api.example.com/upload/batch' , fd . body (), {
headers: { 'Content-Type' : 'multipart/form-data; boundary=' + fd . boundary },
});
}
Best Practices
Load Files Once
Load files in init context, not in the VU function:
// Good: Load once per VU
const file = open ( './large-file.bin' , 'b' );
export default function () {
http . post ( url , { file: http . file ( file , 'file.bin' ) });
}
// Bad: Loads file every iteration
export default function () {
const file = open ( './large-file.bin' , 'b' ); // Don't do this!
http . post ( url , { file: http . file ( file , 'file.bin' ) });
}
Set Appropriate Timeouts
Large uploads may take time:
export const options = {
httpDebug: 'full' , // Debug upload issues
};
const res = http . post ( url , data , {
timeout: '60s' , // Allow up to 60 seconds for upload
});
Validate Upload Results
Always check upload success:
check ( res , {
'upload successful' : ( r ) => r . status === 200 || r . status === 201 ,
'has file ID' : ( r ) => r . json ( 'fileId' ) !== undefined ,
'correct file size' : ( r ) => r . json ( 'size' ) === file . length ,
});
API Reference
Key Functions
open(filePath, [mode]) - Load file contents. Use "b" for binary mode
http.file(data, [filename], [contentType]) - Wrap data as FileData for multipart requests
FormData.append(name, value) - Add fields to multipart form
HTTP Authentication Add authentication to upload requests
API CRUD Operations Complete API testing workflows
API Reference: http.file() Complete http.file() documentation
API Reference: open() Complete open() documentation