You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PowerShell调用Confluence REST API上传附件遇权限及空文件问题求助

PowerShell调用Confluence API上传Excel附件权限及空文件问题解决

问题场景

我正在编写PowerShell脚本,通过Token认证调用Atlassian Confluence REST API将Excel文档上传至指定页面作为附件。脚本代码如下:

Write-Host "Start of script"

$userToken = "TOKEN"
$pageId = "PAGE-ID"
$fileName = "All_Employee_List.xlsx"

$rootFolder = "C:\moonfall\Powershell\Server Side\General Server Scripts"
$subFolder = "All_Employee_list\All_Employee_List_SRV_2"
$filePath = Join-Path $rootFolder $subFolder


$wpURL = "https://DOMAIN/rest/api/content/"

$Headers = @{
    'Authorization' = "Bearer $userToken"
    'X-Atlassian-Token' = 'nocheck'
}

# Get the attachment ID if it exists
$uri = $wpURL + $pageId + "/child/attachment?filename=$fileName&expand=body.storage,version,space,ancestors"
$attachment = Invoke-RestMethod -Method GET -Headers $Headers -Uri $uri
$attachmentId = $attachment.results.id

# If the attachment doesn't exist, create it
if (!$attachmentId) {
    $uri = $wpURL + $pageId + "/child/attachment"
    
    #after this file path check it won't upload to confluence anymore, it just says access to the path is denied
    if (-not (Test-Path $filePath)) {
        Write-Error "File does not exist at $filePath"
        return
    }
    
    try {
        $fileBytes = [System.IO.File]::ReadAllBytes(${filePath})
    } catch {
        Write-Error "Failed to read file at ${filePath}: $_"
        return
    }

    $fileEncoded = [System.Convert]::ToBase64String($fileBytes)

    $delimiter = [System.Guid]::NewGuid().ToString()
    $LF = "`r`n"
    $bodyData = ( 
        "--$delimiter",
        "Content-Disposition: form-data; name=`"file`"; filename=`"$fileName`"",
        "Content-Type: application/octet-stream$LF",
        $fileEncoded,
        "--$delimiter--$LF" 
    ) -join $LF

    Invoke-RestMethod -Uri $uri -Method POST -ContentType "multipart/form-data; boundary=$delimiter" -Headers $Headers -Body $bodyData
    Write-Output "Attachment created successfully."
} else {
    Write-Output "Attachment already exists. Skipping creation."
}

# Set the attachment as the primary viewable attachment
$uri = $wpURL + $attachmentId + "/?minorEdit=true"
$bodyData = @{
    "id" = $attachmentId
    "version" = @{
        "number" = $attachment.version.number + 1
    }
    "type" = "attachment"
    "title" = $fileName
    "container" = @{
        "id" = $pageId
        "type" = "page"
    }
    "metadata" = @{
        "comment" = @{
            "value" = "Automatically uploaded from PowerShell script."
        }
        "labels" = @()
        "properties" = @{
            "download" = @{
                "value" = @{
                    "downloadAll" = "true"
                }
            }
        }
    }
    "extensions" = @{
        "mediaType" = @{
            "value" = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
            "macroEnabled" = "true"
        }
    }
    "status" = "current"
} | ConvertTo-Json

# Make the request
$response = Invoke-RestMethod -Uri $uri -Method POST -Headers $Headers -ContentType "multipart/form-data; boundary=$delimiter" -Body $bodyData

# Print the response
$response

错误1:管理员权限运行时的权限拒绝

以管理员权限运行脚本时,出现错误:

Failed to read file at 'FILE-PATH': Exception calling "ReadAllBytes" with "1" argument(s): "Access to the path 'FILE-PATH' is denied.

错误2:移除文件检查后的空文件与415错误

移除文件读取检查后,出现以下错误,且附件上传后为0KB空文件:

Exception calling "ReadAllBytes" with "1" argument(s): "Access to the path 'C:\moonfall\Powershell\Server Side\General Server Scripts\All_Employee_list\All_Employee_List_SRV_2' is denied."
At line:29 char:5
+     $fileBytes = [System.IO.File]::ReadAllBytes($filePath)
+     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : UnauthorizedAccessException
 
Exception calling "ToBase64String" with "1" argument(s): "Value cannot be null.
Parameter name: inArray"
At line:30 char:5
+     $fileEncoded = [System.Convert]::ToBase64String($fileBytes)
+     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : ArgumentNullException


results                                                                                                                                               size _links
-------                                                                                                                                               ---- ------
{@{id=123455572; type=attachment; status=current; title=All_Employee_List.xlsx; version=; container=; metadata=; extensions=; _links=; _expandable=}}    1 @{base=https://DOMAIN; context=}
Attachment created successfully.
Invoke-RestMethod : The remote server returned an error: (415).
At line:84 char:13
+ $response = Invoke-RestMethod -Uri $uri -Method POST -Headers $Header ...
+             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-RestMethod], WebException
    + FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeRestMethodCommand

解决步骤

1. 修复文件路径错误

核心问题:$filePath仅拼接了文件夹路径,未包含文件名,导致尝试读取文件夹而非文件,触发权限拒绝。修改路径拼接:

# 正确拼接完整文件路径
$filePath = Join-Path $rootFolder $subFolder $fileName

2. 简化Multipart文件上传

手动处理Base64编码和Multipart格式容易出错,直接用Invoke-RestMethod的-InFile参数更可靠。替换原文件读取及上传代码段:

# 替换原有的文件读取、Base64编码及POST请求部分
if (-not (Test-Path $filePath -PathType Leaf)) {
    Write-Error "File does not exist at $filePath"
    return
}

Invoke-RestMethod -Uri $uri -Method POST -Headers $Headers -ContentType "multipart/form-data" -InFile $filePath

3. 修正附件更新请求的方法与ContentType

更新附件属性需用PUT方法,且发送JSON数据时ContentType应为application/json,而非multipart/form-data,这是415错误的根源:

# 修改更新请求的方法和ContentType
$response = Invoke-RestMethod -Uri $uri -Method PUT -Headers $Headers -ContentType "application/json" -Body $bodyData

4. 完善附件存在性检查逻辑

原代码中$attachment.results.id在多个匹配附件时会返回数组,需调整为取第一个匹配项:

$attachmentId = $attachment.results | Where-Object { $_.title -eq $fileName } | Select-Object -ExpandProperty id -First 1

完整修正后的脚本

Write-Host "Start of script"

$userToken = "TOKEN"
$pageId = "PAGE-ID"
$fileName = "All_Employee_List.xlsx"

$rootFolder = "C:\moonfall\Powershell\Server Side\General Server Scripts"
$subFolder = "All_Employee_list\All_Employee_List_SRV_2"
# 正确拼接完整文件路径
$filePath = Join-Path $rootFolder $subFolder $fileName

$wpURL = "https://DOMAIN/rest/api/content/"

$Headers = @{
    'Authorization' = "Bearer $userToken"
    'X-Atlassian-Token' = 'nocheck'
}

# Get the attachment ID if it exists
$uri = "$wpURL$pageId/child/attachment?filename=$fileName&expand=version"
$attachment = Invoke-RestMethod -Method GET -Headers $Headers -Uri $uri
$existingAttachment = $attachment.results | Where-Object { $_.title -eq $fileName } | Select-Object -First 1
$attachmentId = $existingAttachment.id

# If the attachment doesn't exist, create it
if (!$attachmentId) {
    $uri = "$wpURL$pageId/child/attachment"
    
    if (-not (Test-Path $filePath -PathType Leaf)) {
        Write-Error "File does not exist at $filePath"
        return
    }
    
    try {
        Invoke-RestMethod -Uri $uri -Method POST -Headers $Headers -ContentType "multipart/form-data" -InFile $filePath
        Write-Output "Attachment created successfully."
        # 重新获取新创建的附件ID用于后续更新
        $attachment = Invoke-RestMethod -Method GET -Headers $Headers -Uri "$wpURL$pageId/child/attachment?filename=$fileName&expand=version"
        $existingAttachment = $attachment.results | Where-Object { $_.title -eq $fileName } | Select-Object -First 1
        $attachmentId = $existingAttachment.id
    } catch {
        Write-Error "Failed to upload attachment: $_"
        return
    }
} else {
    Write-Output "Attachment already exists. Preparing to update."
}

# Set the attachment as the primary viewable attachment
$uri = "$wpURL$attachmentId/?minorEdit=true"
$bodyData = @{
    "id" = $attachmentId
    "version" = @{
        "number" = $existingAttachment.version.number + 1
    }
    "type" = "attachment"
    "title" = $fileName
    "container" = @{
        "id" = $pageId
        "type" = "page"
    }
    "metadata" = @{
        "comment" = @{
            "value" = "Automatically uploaded from PowerShell script."
        }
        "labels" = @()
        "properties" = @{
            "download" = @{
                "value" = @{
                    "downloadAll" = "true"
                }
            }
        }
    }
    "extensions" = @{
        "mediaType" = @{
            "value" = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
            "macroEnabled" = "true"
        }
    }
    "status" = "current"
} | ConvertTo-Json -Depth 10

# Make the request with correct method and ContentType
try {
    $response = Invoke-RestMethod -Uri $uri -Method PUT -Headers $Headers -ContentType "application/json" -Body $bodyData
    # Print the response
    $response
    Write-Output "Attachment updated successfully."
} catch {
    Write-Error "Failed to update attachment: $_"
}

内容的提问来源于stack exchange,提问作者r4ymaster

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.27 18:07:23