This guide shows how to handle file uploads in your HyperFlow-powered application, including displaying uploaded files and accessing them via URLs.
HyperFlow supports file uploads through the Control API. When a flow-graph requires file input, it returns a fileUpload interaction spec. The SDK provides:
uploadFile() method to send filesgetFileURL() helper to generate download/display URLsimport { useHyperFlow } from '@hyperflow/client-sdk/react';
function ChatBot() {
const {
fileUploadInteractionSpec,
uploadFile,
getFileURL,
messages
} = useHyperFlow({
baseURL: '<https://hyperflow-ai.com>',
apiKey: 'hf_...',
flowGraphID: 'your-flow-graph-id'
});
const handleFileSelect = async (event) => {
const file = event.target.files[0];
if (!file) return;
// Upload to HyperFlow (returns file ID in uploads array)
const formData = new FormData();
formData.append('file', file);
const uploadResponse = await fetch(`${baseURL}/api/hyperflow/file/upload`, {
method: 'POST',
headers: { 'X-API-Key': apiKey },
body: formData
});
const { fileID } = await uploadResponse.json();
// Send to flow-graph
await uploadFile(file, [{ data: fileID }], file.name);
};
return (
<div>
{fileUploadInteractionSpec && (
<input type="file" onChange={handleFileSelect} />
)}
</div>
);
}
The SDK automatically adds uploaded files to chat history with URLs:
function MessageList({ messages, getFileURL }) {
return (
<div>
{messages.map((msg, idx) => {
if (msg.type === 'user' && msg.uploads) {
return (
<div key={idx}>
<p>{msg.text}</p>
{msg.uploads.map((upload, i) => (
<div key={i}>
{upload.mimetype?.startsWith('image/') ? (
<img src={upload.url} alt={upload.fileName} />
) : (
<a href={upload.url} download>
Download {upload.fileName}
</a>
)}
</div>
))}
</div>
);
}
// ... other message types
})}
</div>
);
}
function ChatBot() {
const { messages, getFileURL } = useHyperFlow({...});
return <MessageList messages={messages} getFileURL={getFileURL} />;
}
For custom handling or non-chat displays:
function FileGallery({ uploads, getFileURL }) {
return (
<div className="gallery">
{uploads.map((upload) => {
const url = getFileURL(upload.data);
return (
<div key={upload.data}>
<img src={url} alt={upload.fileName} />
<a href={url} download={upload.fileName}>
Download
</a>
</div>
);
})}
</div>
);
}
import { useHyperFlow } from '@hyperflow/client-sdk/react';
import { useState } from 'react';
function ImageChatBot() {
const {
messages,
fileUploadInteractionSpec,
uploadFile,
sendMessage,
isReady,
getFileURL
} = useHyperFlow({
baseURL: '<https://hyperflow-ai.com>',
apiKey: 'hf_...',
flowGraphID: 'image-analysis-bot'
});
const [previewURL, setPreviewURL] = useState(null);
const handleFileSelect = async (event) => {
const file = event.target.files[0];
if (!file || !file.type.startsWith('image/')) {
alert('Please select an image file');
return;
}
// Show local preview
const localURL = URL.createObjectURL(file);
setPreviewURL(localURL);
try {
// Upload to HyperFlow
const formData = new FormData();
formData.append('file', file);
const response = await fetch(
`${baseURL}/api/hyperflow/file/upload`,
{
method: 'POST',
headers: { 'X-API-Key': apiKey },
body: formData
}
);
const { fileID } = await response.json();
// Send to flow-graph
// SDK will automatically add URL to chat history
await uploadFile(file, [{
data: fileID,
fileName: file.name,
mimetype: file.type,
size: file.size
}], file.name);
} catch (error) {
console.error('Upload failed:', error);
alert('Failed to upload file');
} finally {
// Clean up preview
URL.revokeObjectURL(localURL);
setPreviewURL(null);
}
};
return (
<div className="chat-container">
{/* Messages */}
<div className="messages">
{messages.map((msg, idx) => (
<div key={idx} className={`message ${msg.type}`}>
{msg.text && <p>{msg.text}</p>}
{/* Display uploaded images */}
{msg.uploads && msg.uploads.map((upload, i) => (
<div key={i} className="upload-preview">
{upload.mimetype?.startsWith('image/') ? (
<img
src={upload.url}
alt={upload.fileName}
style={{ maxWidth: '300px' }}
/>
) : (
<a href={upload.url} download={upload.fileName}>
Download {upload.fileName}
</a>
)}
</div>
))}
{/* Display media responses from LLM */}
{msg.media && (
<img src={msg.media.data} alt="Generated image" />
)}
</div>
))}
</div>
{/* File upload input */}
{fileUploadInteractionSpec && (
<div className="file-input">
{previewURL && (
<img
src={previewURL}
alt="Preview"
style={{ maxWidth: '200px' }}
/>
)}
<input
type="file"
accept="image/*"
onChange={handleFileSelect}
/>
</div>
)}
{/* Text input */}
{isReady && (
<input
type="text"
placeholder="Type a message..."
onKeyPress={(e) => {
if (e.key === 'Enter') {
sendMessage(e.currentTarget.value);
e.currentTarget.value = '';
}
}}
/>
)}
</div>
);
}
The SDK handles both upload types automatically: