Improve image upload error handling and validation (#17188)
- Add URL validation in getImageBufferFromUrl utility - Add response status validation and content-type checking - Add timeout and connection error handling with specific error messages - Validate buffer is not empty before processing - Validate file type detection results before proceeding - Ensure detected file type is actually an image format - Add proper type safety for Axios error handling This improves robustness when uploading images from URLs by: - Preventing invalid URLs from being processed - Providing clear error messages for different failure scenarios - Ensuring only valid image files are processed - Handling network errors gracefully --------- Co-authored-by: GitTensor Miner <miner@gittensor.io>
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { type Axios } from 'axios';
|
||||
import { type Axios, type AxiosError } from 'axios';
|
||||
|
||||
const cropRegex = /([w|h])([0-9]+)/;
|
||||
|
||||
@@ -26,9 +26,68 @@ export const getImageBufferFromUrl = async (
|
||||
url: string,
|
||||
axiosInstance: Axios,
|
||||
): Promise<Buffer> => {
|
||||
const response = await axiosInstance.get(url, {
|
||||
responseType: 'arraybuffer',
|
||||
});
|
||||
if (!url || typeof url !== 'string' || url.trim().length === 0) {
|
||||
throw new Error('Invalid URL provided: URL must be a non-empty string');
|
||||
}
|
||||
|
||||
return Buffer.from(response.data, 'binary');
|
||||
try {
|
||||
const response = await axiosInstance.get(url, {
|
||||
responseType: 'arraybuffer',
|
||||
validateStatus: (status) => status >= 200 && status < 300,
|
||||
maxRedirects: 5,
|
||||
timeout: 30000,
|
||||
});
|
||||
|
||||
if (!response.data) {
|
||||
throw new Error('Received empty response from image URL');
|
||||
}
|
||||
|
||||
const bufferLength = Buffer.isBuffer(response.data)
|
||||
? response.data.length
|
||||
: response.data.byteLength;
|
||||
|
||||
if (bufferLength === 0) {
|
||||
throw new Error('Received empty response from image URL');
|
||||
}
|
||||
|
||||
const contentType = response.headers['content-type'];
|
||||
|
||||
if (contentType && !contentType.startsWith('image/')) {
|
||||
throw new Error(
|
||||
`Invalid content type: expected image/*, got ${contentType}`,
|
||||
);
|
||||
}
|
||||
|
||||
return Buffer.from(response.data, 'binary');
|
||||
} catch (error) {
|
||||
const axiosError = error as AxiosError;
|
||||
const axiosResponse = axiosError.response;
|
||||
|
||||
if (axiosResponse) {
|
||||
throw new Error(
|
||||
`Failed to fetch image: HTTP ${axiosResponse.status} from ${url}`,
|
||||
);
|
||||
}
|
||||
|
||||
if (error instanceof Error) {
|
||||
if (
|
||||
axiosError.code === 'ECONNABORTED' ||
|
||||
error.message.includes('timeout')
|
||||
) {
|
||||
throw new Error(
|
||||
`Request timeout while fetching image from URL: ${url}`,
|
||||
);
|
||||
}
|
||||
if (
|
||||
axiosError.code === 'ENOTFOUND' ||
|
||||
axiosError.code === 'ECONNREFUSED' ||
|
||||
error.message.includes('ENOTFOUND') ||
|
||||
error.message.includes('ECONNREFUSED')
|
||||
) {
|
||||
throw new Error(`Failed to connect to image URL: ${url}`);
|
||||
}
|
||||
throw new Error(`Failed to fetch image from URL: ${error.message}`);
|
||||
}
|
||||
throw new Error(`Failed to fetch image from URL: ${url}`);
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user