Skip to main content

URL to Attachment


Forum|alt.badge.img+5

I have registration forms that collect a digital signature. It imports into airtable as a url, like this: (https://s3.amazonaws.com/files.formstack.com/uploads/3669579/85172110/582992994/signature_85172110.png)

Can someone help me with a script that would convert this to an image attachment?

48 replies

Forum|alt.badge.img+3

I’m not a developer, but I know a script - from a toolkit (paid plan, however) - that’ll fix you problem : https://miniextensions.com/convert-urls-to-attachments/ ( from miniextensions.com ).

Hope that’s helpful! :slightly_smiling_face:


Forum|alt.badge.img+19

You can write a URL to an attachment field, like so:

// Change the names of this table/fields according to your base.
let submissionsTable = base.getTable('Form submissions');
let urlField = submissionsTable.getField('Signature URL');
let attachmentField = submissionsTable.getField('Signature attachment');

let submissionsQuery = await submissionsTable.selectRecordsAsync();
let updates = [];
for (let record of submissionsQuery.records) {
    let url = record.getCellValue(urlField);
    let attachments = record.getCellValue(attachmentField);

    // If this record already has an attachment, skip it.
    if (attachments !== null) {
        continue;
    }

    // Otherwise, attach the image at the URL.
    updates.push({
        id: record.id,
        fields: {
            [attachmentField.id]: [{url: url}]
        }
    });
}

// Update records in batches of 50.
while (updates.length > 0) {
    await submissionsTable.updateRecordsAsync(updates.slice(0, 50));
    updates = updates.slice(50);
}

Forum|alt.badge.img+5
  • Author
  • New Participant
  • 2 replies
  • April 8, 2020
Stephen_Suen wrote:

You can write a URL to an attachment field, like so:

// Change the names of this table/fields according to your base.
let submissionsTable = base.getTable('Form submissions');
let urlField = submissionsTable.getField('Signature URL');
let attachmentField = submissionsTable.getField('Signature attachment');

let submissionsQuery = await submissionsTable.selectRecordsAsync();
let updates = [];
for (let record of submissionsQuery.records) {
    let url = record.getCellValue(urlField);
    let attachments = record.getCellValue(attachmentField);

    // If this record already has an attachment, skip it.
    if (attachments !== null) {
        continue;
    }

    // Otherwise, attach the image at the URL.
    updates.push({
        id: record.id,
        fields: {
            [attachmentField.id]: [{url: url}]
        }
    });
}

// Update records in batches of 50.
while (updates.length > 0) {
    await submissionsTable.updateRecordsAsync(updates.slice(0, 50));
    updates = updates.slice(50);
}

Thanks Stephen! This is fantastic


Forum|alt.badge.img+19
Aaron_Phoenix wrote:

Thanks Stephen! This is fantastic


Glad to hear you find this snippet helpful! One minor revision — my original post was missing await in the last section of the script. I’ve updated it.


  • Participating Frequently
  • 7 replies
  • April 9, 2020
Stephen_Suen wrote:

You can write a URL to an attachment field, like so:

// Change the names of this table/fields according to your base.
let submissionsTable = base.getTable('Form submissions');
let urlField = submissionsTable.getField('Signature URL');
let attachmentField = submissionsTable.getField('Signature attachment');

let submissionsQuery = await submissionsTable.selectRecordsAsync();
let updates = [];
for (let record of submissionsQuery.records) {
    let url = record.getCellValue(urlField);
    let attachments = record.getCellValue(attachmentField);

    // If this record already has an attachment, skip it.
    if (attachments !== null) {
        continue;
    }

    // Otherwise, attach the image at the URL.
    updates.push({
        id: record.id,
        fields: {
            [attachmentField.id]: [{url: url}]
        }
    });
}

// Update records in batches of 50.
while (updates.length > 0) {
    await submissionsTable.updateRecordsAsync(updates.slice(0, 50));
    updates = updates.slice(50);
}

Hi Stephen,
I am getting an error :frowning:
What am I doing wrong?



Forum|alt.badge.img+19
Roy_Daneman wrote:

Hi Stephen,
I am getting an error :frowning:
What am I doing wrong?



This script assumes that the URLs are always filled. To handle this case, try replacing lines 13-15 with the following:

if (url === null || attachments !== null) {
    continue;
}

This will tell the script to skip records without anything in the W1 field.

Let me know if this works!


  • Participating Frequently
  • 7 replies
  • April 9, 2020
Stephen_Suen wrote:

This script assumes that the URLs are always filled. To handle this case, try replacing lines 13-15 with the following:

if (url === null || attachments !== null) {
    continue;
}

This will tell the script to skip records without anything in the W1 field.

Let me know if this works!


Hi Stephen,
Thank you for the quick response.
It’s not working :frowning: - see attached.
What do you think is wrong?


Forum|alt.badge.img+4
  • Inspiring
  • 192 replies
  • April 9, 2020
Roy_Daneman wrote:

Hi Stephen,
Thank you for the quick response.
It’s not working :frowning: - see attached.
What do you think is wrong?


I think the condition needs to be if (url === null || attachments !== null) {

In other words, skip it if the URL is blank, or if there are already attachments.


  • Participating Frequently
  • 7 replies
  • April 9, 2020

Hi Kasra,
Thank you for you answer.
It seems to “pass” but now it is showing a new error (see attached) - what’s wrong?


Forum|alt.badge.img+4
  • Inspiring
  • 192 replies
  • April 9, 2020

Please paste the code here, it’s hard to tell from the screenshot.


  • Participating Frequently
  • 7 replies
  • April 10, 2020

  • Participating Frequently
  • 7 replies
  • April 10, 2020

  • Participating Frequently
  • 7 replies
  • April 10, 2020
Kasra wrote:

Please paste the code here, it’s hard to tell from the screenshot.


@Kasra What do you think?


Forum|alt.badge.img+4
  • Inspiring
  • 192 replies
  • April 10, 2020

On line 12, it should be:

// If this record already has an attachment, skip it.
if (url === null || attachments !== null) {
  continue;
}

// Otherwise, attach the image at the URL.

Looks like the “continue; }” part got dropped when you updated the script.


  • Participating Frequently
  • 7 replies
  • April 10, 2020
Kasra wrote:

On line 12, it should be:

// If this record already has an attachment, skip it.
if (url === null || attachments !== null) {
  continue;
}

// Otherwise, attach the image at the URL.

Looks like the “continue; }” part got dropped when you updated the script.


Works Great!!!
Thank you so much @Kasra!


Forum|alt.badge.img+5
  • New Participant
  • 4 replies
  • April 11, 2020

Just want to thank you, @Stephen_Suen and @Kasra. I’m new to javascript and have been struggling to figure this out (attempting to adapt half a dozen diff. scripts in the process).

This has solved a huge use case for me: I’m using AirTable to manage metadata for my late father’s design archives, and have been struggling with how to display image thumbnails with each record (without having to manually attach them). This script allows me to host my images on my server and pull the URL into the attachments field to display the image.

It does feel a bit redundant (since AT is now also hosting the attached image on its back-end, I assume), but it certainly solves my dilemma. I so appreciate you both sharing this code.


Forum|alt.badge.img+19
  • Inspiring
  • 3264 replies
  • June 30, 2020
Jess_Sand wrote:

Just want to thank you, @Stephen_Suen and @Kasra. I’m new to javascript and have been struggling to figure this out (attempting to adapt half a dozen diff. scripts in the process).

This has solved a huge use case for me: I’m using AirTable to manage metadata for my late father’s design archives, and have been struggling with how to display image thumbnails with each record (without having to manually attach them). This script allows me to host my images on my server and pull the URL into the attachments field to display the image.

It does feel a bit redundant (since AT is now also hosting the attached image on its back-end, I assume), but it certainly solves my dilemma. I so appreciate you both sharing this code.


Jess,

It’s great to see you are becoming a scripter; the agility and opportunities to advance all data management activities will skyrocket as your scripting proficiency rises.

If you want to eliminate a bit of the redundancy and server hosting, simply create a Google Drive folder where you curate your dad’s content and set it to share to anyone with link. Then use those links to upload attachments into Airtable. This makes it far easier to organize and curate the content without exposing it to the open web where it will be indexed and broadcast to the world. It’s also far easier to upload documents into Drive than over FTP or whatever. Removing documents from the folder (should that be desired) is also easier.


Forum|alt.badge.img+10
Stephen_Suen wrote:

You can write a URL to an attachment field, like so:

// Change the names of this table/fields according to your base.
let submissionsTable = base.getTable('Form submissions');
let urlField = submissionsTable.getField('Signature URL');
let attachmentField = submissionsTable.getField('Signature attachment');

let submissionsQuery = await submissionsTable.selectRecordsAsync();
let updates = [];
for (let record of submissionsQuery.records) {
    let url = record.getCellValue(urlField);
    let attachments = record.getCellValue(attachmentField);

    // If this record already has an attachment, skip it.
    if (attachments !== null) {
        continue;
    }

    // Otherwise, attach the image at the URL.
    updates.push({
        id: record.id,
        fields: {
            [attachmentField.id]: [{url: url}]
        }
    });
}

// Update records in batches of 50.
while (updates.length > 0) {
    await submissionsTable.updateRecordsAsync(updates.slice(0, 50));
    updates = updates.slice(50);
}

Hi Stephen,

was going to asking help about this when luckily run into this solution.

I’d like to copy url from “turl” into “Preview” in this table https://tinyurl.com/y2lov52r

changed the script as such:
let submissionsTable = base.getTable(Video’);
let urlField = submissionsTable.getField(‘turl’);
let attachmentField = submissionsTable.getField(Preview’);

let submissionsQuery = await submissionsTable.selectRecordsAsync();
let updates = ;
for (let record of submissionsQuery.records) {
let url = record.getCellValue(urlField);
let attachments = record.getCellValue(attachmentField);

// If this record already has an attachment, skip it.
if (attachments !== null) {
    continue;
}

// Otherwise, attach the image at the URL.
updates.push({
    id: record.id,
    fields: {
        [attachmentField.id]: [{url: url}]
    }
});

}

// Update records in batches of 50.
while (updates.length > 0) {
await submissionsTable.updateRecordsAsync(updates.slice(0, 50));
updates = updates.slice(50);
}

and get the following error message

SyntaxError: Invalid or unexpected token
on line 1
at s on line 1
at Generator._invoke on line 1
at Generator.forEach.e.(anonymous function) [as next] on line 1
at e on line 1
at l on line 1
on line 1
on line 1

could you please help me out?


Forum|alt.badge.img+19
Paolo_Perrone wrote:

Hi Stephen,

was going to asking help about this when luckily run into this solution.

I’d like to copy url from “turl” into “Preview” in this table https://tinyurl.com/y2lov52r

changed the script as such:
let submissionsTable = base.getTable(Video’);
let urlField = submissionsTable.getField(‘turl’);
let attachmentField = submissionsTable.getField(Preview’);

let submissionsQuery = await submissionsTable.selectRecordsAsync();
let updates = ;
for (let record of submissionsQuery.records) {
let url = record.getCellValue(urlField);
let attachments = record.getCellValue(attachmentField);

// If this record already has an attachment, skip it.
if (attachments !== null) {
    continue;
}

// Otherwise, attach the image at the URL.
updates.push({
    id: record.id,
    fields: {
        [attachmentField.id]: [{url: url}]
    }
});

}

// Update records in batches of 50.
while (updates.length > 0) {
await submissionsTable.updateRecordsAsync(updates.slice(0, 50));
updates = updates.slice(50);
}

and get the following error message

SyntaxError: Invalid or unexpected token
on line 1
at s on line 1
at Generator._invoke on line 1
at Generator.forEach.e.(anonymous function) [as next] on line 1
at e on line 1
at l on line 1
on line 1
on line 1

could you please help me out?


Hi @Paolo_Perrone:

Lines 1 and 3 of your script are missing the single quote before the name of the table and field:

let submissionsTable = base.getTable('Video');

and

let attachmentField = submissionsTable.getField('Preview');

Adding those single quotes back should fix the error you’re seeing.


  • New Participant
  • 4 replies
  • August 1, 2020

Hi! I have sooo enjoyed this script for images. But I have a PDF automatic download link like this: https://www.teacherspayteachers.com/Preview/Noun-Sort-Cut-and-Paste-083291900-1381063936?fn=valentinesdayworksheetspreview.pdf

That is NOT pulling the PDF correctly. Any ideas?


Forum|alt.badge.img+19
  • Inspiring
  • 3264 replies
  • August 1, 2020
espressivo wrote:

Hi! I have sooo enjoyed this script for images. But I have a PDF automatic download link like this: https://www.teacherspayteachers.com/Preview/Noun-Sort-Cut-and-Paste-083291900-1381063936?fn=valentinesdayworksheetspreview.pdf

That is NOT pulling the PDF correctly. Any ideas?


One idea - that URL is not publicly accessible, a requirement of Airtable attachments.


  • New Participant
  • 4 replies
  • August 4, 2020

Ah, makes sense. Ok, thanks!


  • New Participant
  • 4 replies
  • August 5, 2020

Forum|alt.badge.img+19
  • Inspiring
  • 3264 replies
  • August 5, 2020

Viewers do note make it possible for a GET request to access the file. Viewers are for humans; Airtable’s attachment system is a machine.


  • New Participant
  • 4 replies
  • August 6, 2020

Ha, yeah, I know. I want the impossible :slightly_smiling_face:


Reply