200字
微软 SharePoint API文件上传流程(Graph API + Go)
2025-11-30
2025-11-30

前言

写这篇文章是因为最近项目中一个需求,因为业务是海外公司的,他们的文件存储是基于微软旗下的SharePoint,国内貌似用的很少 所以文档也是比较难找,在摸索了几小时后才成功完成这个需求,

操作是需要注意一下几点:

1.需要使用有管理权限的账号登录

2.添加权限后需要点击 代表 xxx 授予管理员权限同意 按钮

3.在使用GraphAPI 获取权限时可能会查询数据为空,需要检测url和权限是否已经正确配置

1. 前置条件

2. 在 Entra ID 注册应用

  1. Azure Portal → Microsoft Entra ID → 应用注册 → 新注册。

  2. 记录 Application (client) ID、Directory (tenant) ID。

  3. 证书和密码 → 新建客户端密码,保存 client secret。

3. 配置 Graph 权限

  • API 权限 → 添加权限 → Microsoft Graph → 应用程序权限。

  • 勾选至少 Sites.ReadWrite.All(可附加 Files.ReadWrite.All)。

  • 点击 Grant admin consent 让管理员同意。(代表xxx授予管理员同意)

  • image-20251130073505357

  • image-20251130073538607

image-20251130073604069

4. 获取 access_token

POST https://login.microsoftonline.com/{TENANT_ID}/oauth2/v2.0/token Header: Content-Type: application/x-www-form-urlencoded Body:

grant_type=client_credentials
client_id=<CLIENT_ID>
client_secret=<CLIENT_SECRET>
scope=https://graph.microsoft.com/.default

返回的 access_token 用于后续所有 Graph 请求。

5. 查 Site ID 与 Projects Drive ID(执行一次即可)

  1. Graph Explorer 登录用户账号后调用:

    GET https://graph.microsoft.com/v1.0/sites/rmkcivilconstruction.sharepoint.com:/sites/RMK-Portal?$select=id,webUrl

    记录完整 id → SITE_ID。

  2. 查 Projects 列表:

    GET https://graph.microsoft.com/v1.0/sites/{SITE_ID}/lists?$filter=displayName eq 'Projects'&$select=id,displayName

    取 id → LIST_ID。

  3. 查 Projects 的 drive:

    GET https://graph.microsoft.com/v1.0/sites/{SITE_ID}/lists/{LIST_ID}/drive

    返回的 id 即 DRIVE_ID,后续上传都使用它。

如 lists 查询不到,可改用 /drives 全量列出;若仍为空,检查 SharePoint 应用访问策略或权限。

6. Go 上传示例(PDF 小于 4MB)

package main
​
import (
    "bytes"
    "encoding/json"
    "fmt"
    "io"
    "net/http"
    "os"
)
​
type tokenResp struct {
    AccessToken string `json:"access_token"`
}
​
func main() {
    tenantID := os.Getenv("AZ_TENANT_ID")
    clientID := os.Getenv("AZ_CLIENT_ID")
    clientSecret := os.Getenv("AZ_CLIENT_SECRET")
    driveID := os.Getenv("SP_DRIVE_ID")
​
    localFile := "report-2025-11-26.pdf"
    remotePath := "2025/11/report-2025-11-26.pdf"
​
    token, err := fetchToken(tenantID, clientID, clientSecret)
    if err != nil {
        panic(err)
    }
​
    if err := uploadToDrive(driveID, remotePath, localFile, token); err != nil {
        panic(err)
    }
​
    fmt.Println("上传成功")
}
​
func fetchToken(tenant, client, secret string) (string, error) {
    url := fmt.Sprintf("https://login.microsoftonline.com/%s/oauth2/v2.0/token", tenant)
    body := []byte(
        "grant_type=client_credentials" +
            "&client_id=" + client +
            "&client_secret=" + secret +
            "&scope=https%3A%2F%2Fgraph.microsoft.com%2F.default",
    )
​
    req, _ := http.NewRequest("POST", url, bytes.NewReader(body))
    req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
​
    resp, err := http.DefaultClient.Do(req)
    if err != nil {
        return "", err
    }
    defer resp.Body.Close()
​
    data, _ := io.ReadAll(resp.Body)
    if resp.StatusCode >= 300 {
        return "", fmt.Errorf("token err %d: %s", resp.StatusCode, string(data))
    }
​
    var tr tokenResp
    if err := json.Unmarshal(data, &tr); err != nil {
        return "", err
    }
    return tr.AccessToken, nil
}
​
func uploadToDrive(driveID, remotePath, localPath, token string) error {
    fileBytes, err := os.ReadFile(localPath)
    if err != nil {
        return err
    }
​
    url := fmt.Sprintf("https://graph.microsoft.com/v1.0/drives/%s/root:/%s:/content", driveID, remotePath)
    req, _ := http.NewRequest("PUT", url, bytes.NewReader(fileBytes))
    req.Header.Set("Authorization", "Bearer "+token)
    req.Header.Set("Content-Type", "application/pdf")
​
    resp, err := http.DefaultClient.Do(req)
    if err != nil {
        return err
    }
    defer resp.Body.Close()
​
    data, _ := io.ReadAll(resp.Body)
    if resp.StatusCode >= 300 {
        return fmt.Errorf("upload err %d: %s", resp.StatusCode, string(data))
    }
    return nil
}

7. 运行所需环境变量

AZ_TENANT_ID=<TENANT_ID>
AZ_CLIENT_ID=<CLIENT_ID>
AZ_CLIENT_SECRET=<CLIENT_SECRET>
SP_DRIVE_ID=<Projects drive id>

8. 常见排查

  • /sites 或 /drives 返回空:检查 Graph 权限是否已 Admin consent,以及 SharePoint 是否对应用开放。

  • itemNotFound:Site/Drive ID 不完整或指向子站点。

  • 大于 4MB 的文件:改用 createUploadSession 分片上传。

微软 SharePoint API文件上传流程(Graph API + Go)
作者
Echo
发表于
2025-11-30
License
CC BY-NC-SA 4.0

评论