# Upload a large video ## When to use Any upload over ~100 MB, or any video where you want chunked transfer that tolerates network interruptions. ## Complete flow `upload_large` reports its result through a **callback**, not a promise, when uploading a local file — `await`ing the call directly resolves immediately with a stream object and your upload result is lost. Wrap it: ```js const fs = require('fs'); const https = require('https'); const cloudinary = require('cloudinary').v2; // reads CLOUDINARY_URL const SAMPLE_VIDEO_URL = 'https://res.cloudinary.com/demo/video/upload/dog.mp4'; /** Use the local video if present; otherwise download the demo sample to that path. */ function ensureVideoExists(videoPath) { return new Promise((resolve, reject) => { if (fs.existsSync(videoPath)) { resolve(videoPath); return; } https.get(SAMPLE_VIDEO_URL, (response) => { if (response.statusCode !== 200) { reject(new Error(`Sample video download failed: HTTP ${response.statusCode}`)); return; } const file = fs.createWriteStream(videoPath); response.pipe(file); file.on('finish', () => file.close(() => resolve(videoPath))); file.on('error', reject); }).on('error', reject); }); } function uploadLargeVideo(videoPath) { return new Promise((resolve, reject) => { cloudinary.uploader.upload_large( videoPath, { resource_type: 'video', // REQUIRED for videos: upload_large defaults to 'raw' chunk_size: 5 * 1024 * 1024, // 5 MB chunks (the minimum Cloudinary accepts) public_id: 'examples/uploaded-large-video', overwrite: true }, (error, result) => (error ? reject(error) : resolve(result)) ); }); } async function main() { const localPath = await ensureVideoExists('./dog.mp4'); const result = await uploadLargeVideo(localPath); console.log(result.public_id, result.duration, result.bytes); console.log(result.secure_url); // playback URL } main().catch((error) => { const { message } = error.error || error; console.error(`Video upload failed: ${message}`); process.exitCode = 1; }); ``` A remote URL is the exception: `upload_large` delegates to `uploader.upload()` for remote sources, which *does* return a promise. Use the callback form for both so one code path handles either input. ## Asynchronous processing Large or busy videos may finish **processing** after the upload completes. For derived versions, pass `eager` transformations with `eager_async: true` and a `notification_url` webhook; the response then includes a pending status until Cloudinary calls your webhook. ## Troubleshooting - Missing `resource_type: 'video'` — the asset lands as `raw` and cannot be transformed or streamed as video. This is the most common mistake with `upload_large`. - `await cloudinary.uploader.upload_large(...)` returning something that is not the upload result — it returned a stream; use the callback wrapper above. - `All parts except EOF-chunk must be larger than 5mb` — `chunk_size` is below the 5 MB minimum. - Timeouts on slow links — reduce chunk size; each chunk retries independently. ## Related - Runnable example: `examples/upload-large-video.js` — works with no arguments; it downloads a sample video from the Cloudinary demo account if none is supplied. - [Transform and deliver a video](transform-and-deliver-video.md) — what to do with it once it is uploaded. - [Video upload guide](https://cloudinary.com/documentation/node_image_and_video_upload.md#node_js_video_upload)