Skip to content

Instantly share code, notes, and snippets.

@IvanAdmaers
Created November 23, 2022 20:22
Show Gist options
  • Select an option

  • Save IvanAdmaers/d333595b56a88ffd94737c9fb05c7f09 to your computer and use it in GitHub Desktop.

Select an option

Save IvanAdmaers/d333595b56a88ffd94737c9fb05c7f09 to your computer and use it in GitHub Desktop.

Revisions

  1. IvanAdmaers created this gist Nov 23, 2022.
    58 changes: 58 additions & 0 deletions checkImageUrl.test.ts
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,58 @@
    import fs from 'fs/promises';
    import path from 'path';
    import axios from 'axios';

    import { checkImageUrl, allowedFormats, requestWaitingTimeInMs } from '..';

    jest.useFakeTimers();

    describe('checkImageUrl', () => {
    it('should return a correct result for jpeg', async () => {
    const isAllowed = allowedFormats.includes('jpeg');

    const jpegImageBuffer = await fs.readFile(
    path.join(__dirname, './images/jpeg-test.jpeg')
    );

    const mockGet = jest.spyOn(axios, 'get');
    mockGet.mockImplementation(() =>
    Promise.resolve({ data: jpegImageBuffer })
    );

    const result = await checkImageUrl('https://tsthisisforyoudear.com/');

    expect(result).toBe(isAllowed);
    });

    it('should return a correct result for webp', async () => {
    const isAllowed = allowedFormats.includes('webp');

    const webpImageBuffer = await fs.readFile(
    path.join(__dirname, './images/webp-test.webp')
    );

    const mockGet = jest.spyOn(axios, 'get');
    mockGet.mockImplementation(() =>
    Promise.resolve({ data: webpImageBuffer })
    );

    const result = await checkImageUrl('https://tsthisisforyoudear.com/');

    expect(result).toBe(isAllowed);
    });

    it('should return null when server not responding', () => {
    const mockGet = jest.spyOn(axios, 'get');
    mockGet.mockImplementation(() => new Promise(() => {}));

    const pendingPromise = checkImageUrl(
    'https://tsthisisforyoudear.com/'
    ).then((result) => {
    expect(result).toBe(null);
    });

    jest.advanceTimersByTime(requestWaitingTimeInMs);

    return pendingPromise;
    });
    });
    64 changes: 64 additions & 0 deletions checkImageUrl.ts
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,64 @@
    import axios from 'axios';
    import AbortController from 'abort-controller';
    import FileType from 'file-type';

    export const allowedFormats = ['jpg', 'jpeg', 'png'];
    export const requestWaitingTimeInMs = 3 * 1000;

    /**
    * This function checks does an image available by url
    */
    export const checkImageUrl = async (url: string): Promise<boolean | null> =>
    new Promise((resolve) => {
    try {
    (async () => {
    const controller = new AbortController();
    const { signal } = controller;

    const timeout = setTimeout(() => {
    controller.abort();
    }, requestWaitingTimeInMs);

    signal.addEventListener('abort', () => {
    console.log(
    `Request to URL ${url} cancelled due to server not responding`
    );

    resolve(null);
    });

    const req = await axios.get(url, {
    responseType: 'arraybuffer',
    signal,
    });

    clearTimeout(timeout);

    const buffer = Buffer.from(req.data, 'binary');

    const fileData = await FileType.fromBuffer(buffer);

    const { ext, mime } = fileData ?? {};

    if (ext === undefined || mime === undefined) {
    return resolve(false);
    }

    const fileType = mime.split('/')[0];

    if (fileType !== 'image') {
    return resolve(false);
    }

    if (allowedFormats.includes(ext) === false) {
    return resolve(false);
    }

    return resolve(true);
    })();
    } catch (error) {
    console.error(error);

    return resolve(null);
    }
    });