从 Office 365 中提取附件

Extracting attachments from Office 365

我需要从 Outlook 365 邮箱中提取附件。

我在此处遵循此示例 (Powershell)

https://gallery.technet.microsoft.com/office/Export-Email-Messages-from-1419bbe9

我明白了 运行没问题。此示例提取整条消息并保存到 .EML 文件。

要获取附件,我可以在 .EML 文件上 运行 一些其他脚本(我真的不想这样做),或者我可以使用 Powershell 直接提取附件

所以我正在尝试使用 powershell,但是正在使用的 API 上的 doco 比我以前使用的要稀疏

基本上我遇到的问题是 示例 代码通过调用此(简化代码)

来保存电子邮件
# not sure if this is relevant. I think so
$itemPropertySet = New-Object Microsoft.Exchange.WebServices.Data.PropertySet(
[Microsoft.Exchange.WebServices.Data.BasePropertySet]::FirstClassProperties,
[Microsoft.Exchange.WebServices.Data.EmailMessageSchema]::MimeContent)
$emailMsg.Load($itemPropertySet)



# This actually saves it
$emailContent = $emailMsg.MimeContent.Content      
[System.IO.File]::WriteAllBytes("$Path$fileName.eml",$emailContent)

但是当我对附件尝试相同类型的代码时,MimeContent.Content 结果为 NULL

认为是因为我需要用正确的参数调用Attachment.Load才能将附件拖到本地?但是文档没有帮助。

所以无论如何,这里有一些经过简化的清理代码。

此代码打印出附件名称,但对于每个附件我都会收到这些错误

Exception calling "Load" with "1" argument(s): "Empty path name is not legal.

这是因为我没有正确调用 Load

Export-OSCEXOEmailAttachment : Exception calling "WriteAllBytes" with "2" argument(s): "Value cannot be null.

这是因为用来保存附件字节的变量是空的。

还有谁能告诉我 - 每次我更改代码时都需要 Remove-ModuleImport-Module 来刷新吗?我怀疑有一种方法可以直接 运行 脚本而不用导入它

代码如下

Function Export-OSCEXOEmailAttachment
{
    [cmdletbinding()]
    Param
    (
        #Define parameters
        [Parameter(Mandatory=$true,Position=1,ValueFromPipeline=$true)]
        [Microsoft.Exchange.WebServices.Data.SearchFolder]$SearchFolder,
        [Parameter(Mandatory=$true,Position=2)]
        [string]$Path,
        [Parameter(Mandatory=$false,Position=3)]
        [int]$PageSize=100,
        [Parameter(Mandatory=$false)]
        [switch]$AllowOverwrite,
        [Parameter(Mandatory=$false)]
        [switch]$KeepSearchFolder
    )
    Begin
    {
        #Verify the existence of exchange service object
        #This bit of code (removed) 
        #validates that variable $exService is initialised


        #Load necessary properties for email messages
        #Not certain what this is for. Does this indicate which particular properties are loaded?
        $itemPropertySet = New-Object Microsoft.Exchange.WebServices.Data.PropertySet(`
                           [Microsoft.Exchange.WebServices.Data.BasePropertySet]::FirstClassProperties,
                           [Microsoft.Exchange.WebServices.Data.EmailMessageSchema]::MimeContent)

        #Load properties for attachments. Do we need to do this to get Mime.Content??
        $attachmentPropertySet = New-Object Microsoft.Exchange.WebServices.Data.PropertySet(`
                           [Microsoft.Exchange.WebServices.Data.BasePropertySet]::FirstClassProperties,
                           [Microsoft.Exchange.WebServices.Data.Attachment]::MimeContent)

    }
    Process
    {
        #Define the view settings in a folder search operation.
        $itemView = New-Object Microsoft.Exchange.WebServices.Data.ItemView($PageSize)

        #Iterate each item in the search folder
        do
        {
            $findResults = $SearchFolder.FindItems($itemView)

            foreach ($findResult in $findResults) {
                #Bind each email with a small set of PropertySet
                $emailMsg = [Microsoft.Exchange.WebServices.Data.EmailMessage]::Bind(`
                            $exService,$findResult.Id)
                $emailMsg.Load($itemPropertySet)

                # Addition to original function: now iterate through attachments
                foreach ($attachment in $emailMsg.Attachments) {
                    $ext = [System.IO.Path]::GetExtension($attachment.Name)

                    if($ext -eq ".xlsx") {
                     $attachment.Load($attachmentPropertySet)
                     $exportPath=$Path + "\" + $attachment.Name
                     Write-Host $exportPath

                     $attachmentContent = $attachment.MimeContent.Content

                     #Export attachment
                     Try
                     {
                        [System.IO.File]::WriteAllBytes($exportPath,$attachmentContent)
                     }
                     Catch
                     {
                        $PSCmdlet.WriteError($_)
                     }
                   }
                }


            }
        } while ($findResults.MoreAvailable)
    }
    End
}

这是使用此处 https://msdn.microsoft.com/EN-US/library/office/dn726695(v=exchg.150).aspx

中的 API 在 C# 中保存附件的代码示例
public static void SaveEmailAttachment(ExchangeService service, ItemId itemId)
{
    // Bind to an existing message item and retrieve the attachments collection.
    // This method results in an GetItem call to EWS.
    EmailMessage message = EmailMessage.Bind(service, itemId, new PropertySet(ItemSchema.Attachments));

    foreach (Attachment attachment in message.Attachments)
    {
        if (attachment is ItemAttachment)
        {
            ItemAttachment itemAttachment = attachment as ItemAttachment;
            itemAttachment.Load(ItemSchema.MimeContent);
            string fileName = "C:\Temp\" + itemAttachment.Item.Subject + ".eml";

            // Write the bytes of the attachment into a file.
            File.WriteAllBytes(fileName, itemAttachment.Item.MimeContent.Content);

            Console.WriteLine("Email attachment name: "+ itemAttachment.Item.Subject + ".eml");
        }
    }
}

这些都是什么'schemas'?问题就在那里,但我不明白

答案在这里:

http://gsexdev.blogspot.com.au/2009/06/downloading-attachments-and-exporting.html

据我所知,它与架构或 属性 集无关。我应该用

$attachment.Content

而不是

$attachment.MimeContent.Content

如果 Content 出现在任何 API 文档中,这可能会更明显。还有为什么 C# 示例使用 MimeContent.Content?对于经验丰富的 Powershell/API 程序员来说,这可能是显而易见的。

这是最终代码的摘录,基于 https://gallery.technet.microsoft.com/office/Export-Email-Messages-from-1419bbe9

中的原始代码
#Iterate each item in the search folder
do
{
    $findResults = $SearchFolder.FindItems($itemView)
    foreach ($findResult in $findResults) {
        #Bind each email with a small set of PropertySet
        $emailMsg = [Microsoft.Exchange.WebServices.Data.EmailMessage]::Bind($exService,$findResult.Id)
        $emailMsg.Load()

        # Iterate through attachments inside email
        foreach ($attachment in $emailMsg.Attachments) {
            $ext = [System.IO.Path]::GetExtension($attachment.Name)

            if($ext -eq ".xlsx") {
              $attachment.Load()
              $exportPath=$Path + "\" + $attachment.Name
              Write-Host $exportPath

              #Export attachment
              Try
              {
                 $file = New-Object System.IO.FileStream($exportPath,[System.IO.FileMode]::Create)
                 $file.Write($attachment.Content,0,$attachment.Content.Length)
                 $file.Close()
              }
              Catch
              {
                 $PSCmdlet.WriteError($_)
              }
           }
        }


    }

    # Once we've gone through this page of items, go to the next page
    $itemView.Offset += $PageSize
} while ($findResults.MoreAvailable)