io.input.file
Prompts the app user to select and upload a file.
The resulting object points to a temporary file that expires after the action finishes running. You can access its contents in your action and optionally persist the file elsewhere if it should live longer.
Beginning with SDK v0.24.0, you may upload the file directly to your own S3-compatible API by providing custom presigned upload and download URLs.
caution
The resulting temporary file expires after the action finishes running. Please persist the file elsewhere if it should live longer, or provide a custom presigned upload URL.
Usage
- TypeScript
- JavaScript
- Python
const file = await io.input.file("Upload a profile photo", {
helpText: "Select an image.",
allowedExtensions: [".jpg", ".jpeg", ".png"],
});
const file = await io.input.file("Upload a profile photo", {
helpText: "Select an image.",
allowedExtensions: [".jpg", ".jpeg", ".png"],
});
file = await io.input.file("Upload a profile photo",
help_text="Select an image.",
allowed_extensions=[".jpg", ".jpeg", ".png"],
)
Multiple files
Starting with v0.37.0
, you can also accept multiple files at a time using
the .multiple()
chained method:
- TypeScript
- JavaScript
- Python
const file = await io.input
.file("Upload post images", {
helpText: "Select images.",
allowedExtensions: [".jpg", ".jpeg", ".png"],
})
.multiple();
const file = await io.input
.file("Upload post images", {
helpText: "Select images.",
allowedExtensions: [".jpg", ".jpeg", ".png"],
})
.multiple();
file = await io.input.file("Upload post images",
help_text="Select images.",
allowed_extensions=[".jpg", ".jpeg", ".png"],
).multiple()
Note that the above still requires at least one file to be provided. To accept zero
or more files, use .multiple().optional()
.
Props
- TypeScript
- JavaScript
- Python
allowedExtensions
Optional
string[]
disabled
Optional
boolean
generatePresignedUrls
Optional
function
({ name: string, type: string }) => Promise<{
uploadUrl: string,
downloadUrl: string,
}>
helpText
Optional
string
Returns
object
extension
string
lastModified
Date
name
string
size
number
type
string
buffer()
() => Promise<Buffer>
json()
() => Promise<any>
text()
() => Promise<string>
url()
() => Promise<string>
allowedExtensions
Optional
string[]
disabled
Optional
boolean
generatePresignedUrls
Optional
function
({ name: string, type: string }) => Promise<{
uploadUrl: string,
downloadUrl: string,
}>
helpText
Optional
string
Returns
object
extension
string
lastModified
Date
name
string
size
number
type
string
buffer()
() => Promise<Buffer>
json()
() => Promise<any>
text()
() => Promise<string>
url()
() => Promise<string>
allowed_extensions
Optional
Iterable[str]
disabled
Optional
bool
generate_presigned_urls
Optional
(FileState) -> FileUrlSet
class FileState:
name: str
type: str
class FileUrlSet(TypedDict):
uploadUrl: str
downloadUrl: str
help_text
Optional
str
Returns
IntervalFile
extension
str
last_modified
datetime
name
str
size
int
type
str
read
() -> bytes
json
() -> Any
text
() -> str
url
str
Examples
Working with text files
Use the file's .text()
function to obtain its text contents.
- TypeScript
- JavaScript
- Python
const file = await io.input.file("Upload a text file", {
allowedExtensions: [".txt", ".md"],
});
// Obtain the file's text contents
const text = await file.text();
const file = await io.input.file("Upload a text file", {
allowedExtensions: [".txt", ".md"],
});
// Obtain the file's text contents
const text = await file.text();
file = await io.input.file(
"Upload a text file",
allowed_extensions=[".txt", ".md"],
)
# Obtain the file's text contents
text = await file.text()
Working with JSON files
If you are working with JSON files, the file's .json()
function will handle
deserialization for you.
- TypeScript
- JavaScript
- Python
const file = await io.input.file("Upload a saved configuration file", {
allowedExtensions: [".json"],
});
// Obtain the file's contents and parse it as JSON
const config = await file.json();
const file = await io.input.file("Upload a saved configuration file", {
allowedExtensions: [".json"],
});
// Obtain the file's contents and parse it as JSON
const config = await file.json();
file = await io.input.file(
"Upload a saved configuration file",
allowed_extensions=[".json"],
)
# Obtain the file's text contents
config = await file.json()
info
This can throw an error if the file does not contain valid JSON.
Consider wrapping calls to .json()
in a try...catch
block
if you want to handle the error.
Working with binary files
To obtain the raw binary data of the file, for example if you're working with
images or video, use the file's .buffer()
function.
- TypeScript
- JavaScript
- Python
const file = await io.input.file("Upload a profile photo", {
allowedExtensions: [".jpg", ".jpeg", ".png"],
});
// Obtain the file's contents as a binary Buffer
const buffer = await file.buffer();
// Save the image contents locally to disk
fs.writeFileSync(`/path/to/file/${image.name}`, buffer);
// Or, upload to S3 (or another file hosting provider)
import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3";
const s3 = new S3Client({ region: "us-west-1" });
await s3.send(
new PutObjectCommand({
Bucket: process.env.S3_BUCKET_NAME,
Key: file.name,
Body: buffer,
})
);
const file = await io.input.file("Upload a profile photo", {
allowedExtensions: [".jpg", ".jpeg", ".png"],
});
// Obtain the file's contents as a binary Buffer
const buffer = await file.buffer();
// Save the image contents locally to disk
fs.writeFileSync(`/path/to/file/${image.name}`, buffer);
// Or, upload to S3 (or another file hosting provider)
import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3";
const s3 = new S3Client({ region: "us-west-1" });
await s3.send(
new PutObjectCommand({
Bucket: process.env.S3_BUCKET_NAME,
Key: file.name,
Body: buffer,
})
);
file = await io.input.file(
"Upload a profile photo",
allowed_extensions=[".jpg", ".jpeg", ".png"],
)
# Obtain the file's contents as binary bytes
contents = file.read()
# Save to a local file
with open(file.name, "wb") as f:
f.write(contents)
Uploading to your own S3 bucket
You can upload files directly to any S3-compatible storage by providing custom upload and download URLs in an optional generatePresignedUrls()
handler:
- TypeScript
- JavaScript
- Python
import { PutObjectCommand, S3Client } from "@aws-sdk/client-s3";
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
const file = await io.input.file("Upload a profile photo", {
allowedExtensions: [".jpg", ".jpeg", ".png"],
generatePresignedUrls: async ({ name }) => {
const path = `profile-photos/${new Date().getTime()}-${name}`;
const s3Client = new S3Client({
region: process.env.AWS_REGION,
credentials: {
accessKeyId: process.env.AWS_KEY_ID,
secretAccessKey: process.env.AWS_KEY_SECRET,
},
});
const putCommand = new PutObjectCommand({
Bucket: process.env.AWS_S3_BUCKET,
Key: path,
});
const uploadUrl = await getSignedUrl(s3Client, putCommand, {
expiresIn: 3600,
});
// The download URL does not need to be presigned
// if your bucket settings allow public access
const url = new URL(uploadUrl);
const downloadUrl = url.origin + url.pathname;
return { uploadUrl, downloadUrl };
},
});
const avatarUrl = await file.url();
await updateUser(userId, { avatarUrl });
import { PutObjectCommand, S3Client } from "@aws-sdk/client-s3";
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
const file = await io.input.file("Upload a profile photo", {
allowedExtensions: [".jpg", ".jpeg", ".png"],
generatePresignedUrls: async ({ name }) => {
const path = `profile-photos/${new Date().getTime()}-${name}`;
const s3Client = new S3Client({
region: process.env.AWS_REGION,
credentials: {
accessKeyId: process.env.AWS_KEY_ID,
secretAccessKey: process.env.AWS_KEY_SECRET,
},
});
const putCommand = new PutObjectCommand({
Bucket: process.env.AWS_S3_BUCKET,
Key: path,
});
const uploadUrl = await getSignedUrl(s3Client, putCommand, {
expiresIn: 3600,
});
// The download URL does not need to be presigned
// if your bucket settings allow public access
const url = new URL(uploadUrl);
const downloadUrl = url.origin + url.pathname;
return { uploadUrl, downloadUrl };
},
});
const avatarUrl = await file.url();
await updateUser(userId, { avatarUrl });
import os, time
import boto3
from interval_sdk import IO, FileState
async def generate_presigned_urls(file: FileState) -> FileUrlSet:
path = f"profile-photos/{time.time_ns()}-{file.name}";
s3_client = boto3.client("s3")
upload_url = s3_client.generate_presigned_url(
ClientMethod="put_object",
Params={
"Bucket": os.environ["S3_BUCKET_NAME"],
"Key": path,
},
ExpiresIn=3600, # 1 hour
HttpMethod="PUT", # Important! Must accept PUT requests
)
url = urlparse(upload_url)
url = url._replace(params="", query="", fragment="")
download_url = url.geturl()
return {
"uploadUrl": upload_url,
"downloadUrl": download_url,
}
file = await io.input.file("Upload a profile photo",
allowed_extensions=[".jpg", ".jpeg", ".png"],
generate_presigned_urls=generate_presigned_urls,
)
avatar_url = await file.url()
await updateUser(user_id, avatar_url=avatar_url)
In order for the upload to be accepted, your S3 bucket's cross-origin resource sharing (CORS) settings must allow PUT requests:
// Example S3 cross-origin resource sharing (CORS) config:
[
{
"AllowedHeaders": [
"*"
],
"AllowedMethods": [
"PUT"
],
"AllowedOrigins": [
"*"
],
"ExposeHeaders": []
}
]