Saving Mail message attachments

Here is a snippet of code showing how to save email message attachments using the Mail application. The script processes all selected messages and saves each attachment to the Desktop:

use AppleScript version "2.4" -- Yosemite (10.10) or later
use scripting additions

--
--	Save attachments of selected Mail messages to the desktop
--

set desktopPath to (path to desktop)'s POSIX path

tell application "Mail"
	repeat with aMessage in (get selection)
		repeat with anAttachment in mail attachments of aMessage
			set attachmentName to name of anAttachment
			
			save anAttachment in POSIX file (desktopPath & attachmentName)
		end repeat
	end repeat
end tell
2 Likes

And if you want to return a value (e.g. a list of all the filePaths saved to)

(to enable notification, for example, or composition within some larger process)

then concatMap (combined with list-wrapping of returned filePaths), allows (by concatenation), for a pruned list – with no ‘empties’ where where selected messages turned out to have no attachments.

use AppleScript version "2.4" -- Yosemite (10.10) or later
use scripting additions

--
--    Save attachments of selected Mail messages to the desktop
--

set folderPath to (path to desktop)'s POSIX path

tell application "Mail"
    script attachmentsSaved
        on |λ|(mailItem)
            script fpSaved
                on |λ|(x)
                    set fp to folderPath & (name of x)
                    save x in POSIX file fp
                    {fp}
                end |λ|
            end script
            concatMap(fpSaved, get mail attachments of mailItem)
        end |λ|
    end script
    
    -- Return value: file paths of any saved files
    my concatMap(attachmentsSaved, get selection)

    --> {"/Users/houthakker/Desktop/labour draft.docx", "/Users/houthakker/Desktop/Socialist_dress_outline.isf"}
end tell


-- GENERIC  ------------------------------------------------------------------

-- concatMap :: (a -> [b]) -> [a] -> [b]
on concatMap(f, xs)
    set lng to length of xs
    set acc to {}
    tell mReturn(f)
        repeat with i from 1 to lng
            set acc to acc & |λ|(item i of xs, i, xs)
        end repeat
    end tell
    return acc
end concatMap

-- Lift 2nd class handler function into 1st class script wrapper
-- mReturn :: First-class m => (a -> b) -> m (a -> b)
on mReturn(f)
    if class of f is script then
        f
    else
        script
            property |λ| : f
        end script
    end if
end mReturn

how is this better than using Mark’s script with this as the fist script line:

set myFilePathList to {}

and putting this in the deepest repeat loop for the attachment move:

set myFilePathList to myFilePathList & posix file of (desktopPath & attachmentName)

?

7 posts were merged into an existing topic: Using map, filter and reduce or fold in Applescript

Hello Mark,

Thanks, I was able to take your script and modify it slightly to function when invoked from a mail rule.

Tim

Oddly enough, when I run this using POSIX file, Mail throws an error that some objects were not the correct type. It works fine, if I change it to this:

`save anAttachment in file (desktopPath & attachmentName)`

The entire script which works on the selection without using POSIX:

set desktopPath to (path to desktop) as string
tell application "Mail"
	set selectedMessages to the selection
	repeat with aMessage in selectedMessages
		repeat with anAttachment in mail attachments of aMessage
			set attachmentName to name of anAttachment
			save anAttachment in file (desktopPath & attachmentName)
		end repeat
	end repeat
end tell

OS X 10.13.6
Mail Version 11.5 (3445.9.1)

Would you mind sharing your final script? I have the same requirement I’ve been struggling with.

Thanks.

I think the rules have changed a bit. Considering folderPath is a path to a file server folder, the mail account is called “Exchange” and the archive folder is called “Archief”:

using terms from application "Mail"
	on perform mail action with messages theDetectedMessages for rule theRule
		
		set folderPath to "Volumes:Public:Scansnap:OA Contracten:test:" as string
		
		tell application "Mail"
			set theMessageCount to count of theDetectedMessages
			repeat with a from 1 to theMessageCount
				set theCurrentMessage to item a of theDetectedMessages
				repeat with theAttachment in mail attachments of theCurrentMessage
					set theAttachmentName to name of theAttachment
					save theAttachment in file (folderPath & theAttachmentName)
				end repeat
				set read status of theCurrentMessage to true
				move theCurrentMessage to (mailbox "Archief" of account "Exchange")
			end repeat
		end tell
		
	end perform mail action with messages
end using terms from

This script files the enclosures, marks the messages as read, and files them. For some reason, letting mail handle those 2 last things instead of the AppleScript, did not work.