Search code examples
powershellsharepointsharepoint-2010odatapowershell-3.0

Upload file to SharePoint 2010 using PowerShell and the OData API


I'm attempting to upload a file to SharePoint 2010:

Function Add-Attachments()
{

    [CmdletBinding()]
    Param(
        [Parameter(Mandatory=$True)]
        [int]$Id,
        [Parameter(Mandatory=$True)]
        [string[]]$Paths
    )

    BEGIN {}
    PROCESS {

        $url = "http://server/resource/_vti_bin/listdata.svc/TheList($Id)/Attachments"

        Foreach ($Path in $Paths) {

            Write-Verbose "Attaching $Path ..."
            $headers = @{
                'Slug' = "TheList|$Id|$(Split-Path $path -Leaf)"
            }

            $Payload = @{filename=(Split-Path $path -Leaf);filecontent=([IO.File]::ReadAllBytes($path))}

            Invoke-WebRequest -Uri $url -Method Post -UseDefaultCredentials -Body $Payload -Headers $headers

        } # Foreach

    } # PROCESS
    END {}

}

Add-Attachments -Id 1234 -Paths 'C:\Users\gandalf\Desktop\test.txt' -verbose

I get an error that reads:

Invoke-WebRequest : An error occurred while processing this request. At C:\Users\gandalf\Documents\WindowsPowerShell\Scripts\SP\SharePoint2010.ps1:382 char:13 + Invoke-WebRequest -Uri $url -Method Post -UseDefaultCredentials -Bod ... + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-WebRequest], WebException + FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeWebRequestCommand

** edit **

The file's original content:

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. Donec et mollis dolor. Praesent et diam eget libero egestas mattis sit amet vitae augue. Nam tincidunt congue enim, ut porta lorem lacinia consectetur. Donec ut libero sed arcu vehicula ultricies a non tortor. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean ut gravida lorem. Ut turpis felis, pulvinar a semper sed, adipiscing id dolor. Pellentesque auctor nisi id magna consequat sagittis. Curabitur dapibus enim sit amet elit pharetra tincidunt feugiat nisl imperdiet. Ut convallis libero in urna ultrices accumsan. Donec sed odio eros. Donec viverra mi quis quam pulvinar at malesuada arcu rhoncus. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. In rutrum accumsan ultricies. Mauris vitae nisi at sem facilisis semper ac in est.

The script does create a file on the server, but with this content:

filename=test.txt&filecontent=Lorem+ipsum+dolor+sit+amet%2c+consectetur+adipiscing+elit.+Donec+a+diam+lectus.+Sed+sit+amet+ipsum+mauris.+Maecenas+congue+ligula+ac+quam+viverra+nec+consectetur+ante+hendrerit.+Donec+et+mollis+dolor.+Praesent+et+diam+eget+libero+egestas+mattis+sit+amet+vitae+augue.+Nam+tincidunt+congue+enim%2c+ut+porta+lorem+lacinia+consectetur.+Donec+ut+libero+sed+arcu+vehicula+ultricies+a+non+tortor.+Lorem+ipsum+dolor+sit+amet%2c+consectetur+adipiscing+elit.+Aenean+ut+gravida+lorem.+Ut+turpis+felis%2c+pulvinar+a+semper+sed%2c+adipiscing+id+dolor.+Pellentesque+auctor+nisi+id+magna+consequat+sagittis.+Curabitur+dapibus+enim+sit+amet+elit+pharetra+tincidunt+feugiat+nisl+imperdiet.+Ut+convallis+libero+in+urna+ultrices+accumsan.+Donec+sed+odio+eros.+Donec+viverra+mi+quis+quam+pulvinar+at+malesuada+arcu+rhoncus.+Cum+sociis+natoque+penatibus+et+magnis+dis+parturient+montes%2c+nascetur+ridiculus+mus.+In+rutrum+accumsan+ultricies.+Mauris+vitae+nisi+at+sem+facilisis+semper+ac+in+est.

What am I missing? Do I need to include the content length? Set the MIME type?


Solution

  • In order to create an attachment resource the following properties have to be specified:

    Endpoint Uri: http://server/site/_vti_bin/listdata.svc/entityset(itemid)/Attachments
    Method: POST
    Headers:
       Slug: "entityset|itemid|name"
       ContentType: */* 
    Body: content
    

    Having said that, my conslution that the specified body parameter ($payload) is invalid in the provided example.

    The following example demonstrates how to upload attachment file via SharePoint 2010 REST Interface:

    Function Add-Attachments()
    {
    
        [CmdletBinding()]
        Param(
            [Parameter(Mandatory=$True)]
            [string]$WebUrl, 
            [Parameter(Mandatory=$True)]
            [string]$ListName, 
            [Parameter(Mandatory=$True)]
            [int]$ItemId,
            [Parameter(Mandatory=$True)]
            [string]$SourcePath
        )
    
        BEGIN {}
        PROCESS {
            $endpointUri = New-Object System.Uri("$WebUrl/_vti_bin/listdata.svc/$ListName($ItemId)/Attachments")
            $fileName = (Split-Path $SourcePath -Leaf)
            $fileContent = ([IO.File]::ReadAllBytes($SourcePath))
            $headers = @{
                     'Slug' = "$ListName|$ItemId|$fileName";
            }
    
            Invoke-WebRequest -Uri $endpointUri -Method Post -UseDefaultCredentials -Body $fileContent -Headers $headers -ContentType "*/*"
    
        } # PROCESS
        END {}
    
    }
    

    Usage:

    Add-Attachments -WebUrl "http://contoso.intranet.com/" -ListName "Tasks" -ItemId 1 -SourcePath "C:\Users\user\Documents\SharePointUserGuide.docx" -verbose
    

    Update

    After performing some analysis via Fiddler, it was determined that the proper endpoint url should be:

    /_vti_bin/listdata.svc/Attachments HTTP/1.1
    

    instead of:

    /_vti_bin/listdata.svc/Tasks(<id>)/Attachments HTTP/1.1
    

    Modified example

    Function Add-Attachments()
    {
    
        [CmdletBinding()]
        Param(
            [Parameter(Mandatory=$True)]
            [string]$WebUrl, 
            [Parameter(Mandatory=$True)]
            [string]$ListName, 
            [Parameter(Mandatory=$True)]
            [int]$ItemId,
            [Parameter(Mandatory=$True)]
            [string]$SourcePath
        )
    
        BEGIN {}
        PROCESS {
            $endpointUri = New-Object System.Uri("$WebUrl/_vti_bin/listdata.svc/Attachments")  
            $fileName = (Split-Path $SourcePath -Leaf)
            $fileContent = ([IO.File]::ReadAllBytes($SourcePath))
            $headers = @{
                     'Slug' = "$ListName|$ItemId|$fileName";
            }
    
            Invoke-WebRequest -Uri $endpointUri -Method Post -UseDefaultCredentials -Body $fileContent -Headers $headers -ContentType "*/*"
    
        } # PROCESS
        END {}
    
    }