import requests
from msal import PublicClientApplication
from dotenv import load_dotenv
import os
# Load environment variables
load_dotenv()
# Configuration from environment variables
CLIENT_ID = os.getenv('CLIENT_ID')
# TENANT_ID is not used since we're using the 'consumers' endpoint
CLIENT_SECRET = os.getenv('CLIENT_SECRET') # This might not be necessary for public clients
USER_ID = os.getenv('USER_ID')
authority_url = 'https://login.microsoftonline.com/consumers'
scopes = ['Files.ReadWrite.All'] # Scope 'https://graph.microsoft.com/.default' might not be needed
app = PublicClientApplication(CLIENT_ID, authority=authority_url)
# The following is a simplistic approach to illustrate the flow
# You need a URL to redirect the user to for login, including the redirect URI
redirect_uri = 'http://localhost:8000/callback' # Make sure this matches the redirect URI configured in Azure
url = app.get_authorization_request_url(scopes, redirect_uri=redirect_uri)
print("Please go to this URL and sign-in:", url)
# After sign-in, you will receive a callback to the redirect URI with a code
# This part typically happens on your web server which handles the redirect
code = input("Enter the code you received: ")
result = app.acquire_token_by_authorization_code(code, scopes=scopes, redirect_uri=redirect_uri)
if 'access_token' in result:
access_token = result['access_token']
else:
print(result.get('error'))
print(result.get('error_description'))
def upload_file(access_token, file_path, destination_path):
headers = {
'Authorization': 'Bearer ' + access_token,
'Content-Type': 'application/octet-stream'
}
with open(file_path, 'rb') as file_data:
response = requests.put(
f'https://graph.microsoft.com/v1.0/users/{USER_ID}/drive/root:/{destination_path}:/content',
headers=headers,
data=file_data
)
return response.json()
我有一个个人 Microsoft 帐户,此代码使用 microsoft GRAPH API 将我的图像和至少 2GB 的 zip 文件上传到 onedrive。但是,我尝试做的事情却从未获得要上传的访问令牌。我不知道我错过了什么。
提供的代码看起来像是用于将文件上传到 OneDrive 的正确起点,但是在获取访问令牌时遇到问题,这表明身份验证流程中存在问题。让我们一步一步地解决这个问题,并探讨针对大型文件的一些重要改进。
1. 理解身份验证流程
的代码使用的是 OAuth 2.0 授权码流,这对于 Web 应用程序和需要更高安全级别的脚本来说非常适合。以下是工作原理:
a.
请求授权码:
的代码首先会构建一个 URL,并将用户重定向到 Microsoft 身份平台以进行登录。
b.
用户登录并授权:
用户在浏览器中访问该 URL,登录到其 Microsoft 帐户,并授予的应用程序访问其 OneDrive 的权限。
c.
接收授权码:
用户登录并授予权限后,Microsoft 会将其重定向到指定的
redirect_uri
,并在 URL 参数中包含一个
code
。
d.
交换代码以获取访问令牌:
的代码需要接收此
code
并将其与的客户端密钥一起发送到 Microsoft 的令牌端点,以换取访问令牌。
遇到的问题很可能出现在步骤 c 和 d 中,因为无法接收代码。
2. 调试身份验证
-
验证的
redirect_uri
: 确保在 Azure 门户中为应用程序注册的redirect_uri
与代码中使用的redirect_uri
(http://localhost:8000/callback
)完全匹配。不匹配会导致重定向失败。 - 检查浏览器控制台: 当被重定向到 Microsoft 登录页面并返回时,请检查浏览器的开发人员工具控制台,以获取可能揭示重定向问题的任何错误消息或警告。
-
使用 Flask 或 Django 之类的简单 Web 框架: 为的重定向 URI 设置一个简单的 Web 服务器,以处理来自 Microsoft 的回调。这将使能够更可靠地接收和处理授权代码。
```python from flask import Flask, request, redirect # ... other imports
app = Flask( name )
@app.route('/callback') def handle_callback(): code = request.args.get('code') if code: # Now you have the code, proceed to exchange it for an access token result = app.acquire_token_by_authorization_code(code, scopes=scopes, redirect_uri=redirect_uri) # ... handle the result and proceed with file upload else: return "Error: Authorization code not found in request" return "Authorization successful"
if name == ' main ': app.run(debug=True, port=8000) ```
3. 处理大型文件上传
对于 2GB 或更大的文件,直接上传整个文件可能会遇到超时或内存问题。OneDrive API 支持分段上传,可以将大文件分解为更小的块进行上传。
以下是使用
requests
库执行分段上传的基本步骤:
- 创建上传会话: 向 OneDrive API 发送一个请求,以创建一个新的上传会话。这将返回一个上传 URL,将使用该 URL 将文件块上传到该 URL。
- 将文件分割成块: 将的文件分割成大小相等的块(例如,5MB)。
-
逐块上传:
使用
requests
库中的put
方法,将每个块上传到 OneDrive API 提供的上传 URL。确保为每个块请求包含适当的 Content-Range 标头。 - 完成上传: 上传所有块后,向 OneDrive API 发送一个请求以完成上传会话。
4. 代码改进
以下是对的代码的一些建议改进,以实现分段上传:
import requests
from msal import PublicClientApplication
from dotenv import load_dotenv
import os
import math
# ... (load environment variables and configure app as before) ...
def upload_large_file(access_token, file_path, destination_path, chunk_size=5242880): # 5MB chunks
file_size = os.path.getsize(file_path)
headers = {
'Authorization': 'Bearer ' + access_token
}
# 1. Create upload session
create_session_url = f'https://graph.microsoft.com/v1.0/users/{USER_ID}/drive/root:/{destination_path}:/createUploadSession'
response = requests.post(create_session_url, headers=headers)
response.raise_for_status() # Check for errors
upload_url = response.json()['uploadUrl']
# 2. Upload file in chunks
with open(file_path, 'rb') as file_data:
total_chunks = math.ceil(file_size / chunk_size)
for chunk_num in range(total_chunks):
start_pos = chunk_num * chunk_size
end_pos = min((chunk_num + 1) * chunk_size, file_size)
chunk_data = file_data.read(chunk_size)
content_range = f'bytes {start_pos}-{end_pos-1}/{file_size}'
headers['Content-Range'] = content_range
response = requests.put(upload_url, headers=headers, data=chunk_data)
response.raise_for_status() # Check for errors
print(f'Uploaded chunk {chunk_num+1}/{total_chunks}')
print("File uploaded successfully!")
# ... (rest of your code, using upload_large_file function) ...
请记住,需要将占位符值(如的客户端 ID、租户 ID 和重定向 URI)替换为自己的值。此外,确保在 Azure 门户中为的应用程序注册了适当的 API 权限(
Files.ReadWrite.All
)。