ai
Version:
AI SDK by Vercel - build apps like ChatGPT, Claude, Gemini, and more with a single interface for any model using the Vercel AI Gateway or go direct to OpenAI, Anthropic, Google, or any other model provider.
75 lines (68 loc) • 1.8 kB
text/typescript
import {
DownloadError,
readResponseWithSizeLimit,
DEFAULT_MAX_DOWNLOAD_SIZE,
validateDownloadUrl,
} from '@ai-sdk/provider-utils';
import {
withUserAgentSuffix,
getRuntimeEnvironmentUserAgent,
} from '@ai-sdk/provider-utils';
import { VERSION } from '../../version';
/**
* Download a file from a URL.
*
* @param url - The URL to download from.
* @param maxBytes - Maximum allowed download size in bytes. Defaults to 100 MiB.
* @param abortSignal - An optional abort signal to cancel the download.
* @returns The downloaded data and media type.
*
* @throws DownloadError if the download fails or exceeds maxBytes.
*/
export const download = async ({
url,
maxBytes,
abortSignal,
}: {
url: URL;
maxBytes?: number;
abortSignal?: AbortSignal;
}) => {
const urlText = url.toString();
validateDownloadUrl(urlText);
try {
const response = await fetch(urlText, {
headers: withUserAgentSuffix(
{},
`ai-sdk/${VERSION}`,
getRuntimeEnvironmentUserAgent(),
),
signal: abortSignal,
});
// Validate final URL after redirects to prevent SSRF via open redirect
if (response.redirected) {
validateDownloadUrl(response.url);
}
if (!response.ok) {
throw new DownloadError({
url: urlText,
statusCode: response.status,
statusText: response.statusText,
});
}
const data = await readResponseWithSizeLimit({
response,
url: urlText,
maxBytes: maxBytes ?? DEFAULT_MAX_DOWNLOAD_SIZE,
});
return {
data,
mediaType: response.headers.get('content-type') ?? undefined,
};
} catch (error) {
if (DownloadError.isInstance(error)) {
throw error;
}
throw new DownloadError({ url: urlText, cause: error });
}
};