vgrem / Office365-REST-Python-Client

Microsoft 365 & Microsoft Graph Library for Python
MIT License
1.32k stars 333 forks source link

Issue with Uploading Files to SharePoint: Metadata Not Setting After Successful Upload #903

Closed AGD-Fersa closed 3 days ago

AGD-Fersa commented 4 days ago

This is my actual code:

from office365.sharepoint.client_context import ClientContext
import base64

# Parameters
URL = 'https://fersad365.sharepoint.com/sites/MaterialTestSite/'  # SharePoint site URL
RELATIVE_URL = 'test_collection/'  # Relative URL of the target folder in SharePoint
USER = 'your_username'  # SharePoint username
PASSWORD = 'your_password'  # SharePoint password
data = startTrigger.data  # Retrieve data directly from the trigger
FILE = data['file']  # File data from the trigger
META = data['meta']  # Metadata from the trigger

# Functions
def get_sharepoint_context(url, user, password):
    """Establish a connection to SharePoint using provided credentials."""
    ctx = ClientContext(url).with_user_credentials(user, password)
    return ctx

def get_target_folder(ctx, relative_url):
    """Retrieve the target folder in SharePoint by its server-relative URL."""
    target_folder = ctx.web.get_folder_by_server_relative_url(relative_url)
    return target_folder

def upload_file_to_sharepoint(url, relative_url, user, password, file, meta):
    """Upload a file to SharePoint and add associated metadata."""
    ctx = get_sharepoint_context(url, user, password)
    target_folder = get_target_folder(ctx, relative_url)

    file_content = base64.b64decode(file['base64Data'])  # Decode the base64-encoded file data
    file_name = file['name']  # Get the file name

    # Upload the file to SharePoint
    uploaded_file = target_folder.upload_file(file_name, file_content).execute_query()

    # Add metadata to the uploaded file
    file_item = uploaded_file.file.listitem_allfields
    file_item.set_property('ID', meta['id'])
    file_item.set_property('Location', meta['location'])
    file_item.set_property('Month', meta['date']['month'])
    file_item.set_property('Year', meta['date']['year'])
    file_item.set_property('Supplier', meta['supplier'])
    file_item.set_property('Batch', meta['batch'])
    file_item.set_property('Status', meta['status'])
    file_item.set_property('Analysis', meta['analysis'])

    # Save changes to the file item
    file_item.update()
    ctx.execute_query()  # Execute the query to apply changes

# Call the function to upload the file and metadata
upload_file_to_sharepoint(URL, RELATIVE_URL, USER, PASSWORD, FILE, META)

I am currently working on a Retool project. The objective is to enable users to complete a form and upload a file, which will then be sent to SharePoint along with the form metadata. In SharePoint, I have configured several columns in my collection to align with the metadata of the files. It is my objective that, upon uploading a file, the metadata is directly linked to the columns in SharePoint. This will enable users to access SharePoint and filter or sort files based on the metadata of the files. I am attempting to complete this process using Python code within a workflow. While the code is able to successfully upload the files to SharePoint, it does not set the metadata. The workflow receives a JSON similar to the following example, and I have verified in the logs that the data is reaching the Python code, indicating that the issue is not with the data transfer:

{
    "file": {
        "name": {{ fileInput1.file.name }},
        "base64Data": {{ fileInput1.file.uri.substring(fileInput1.file.uri.indexOf("base64,") + 7) }}
    }, "meta": {
        "id": {{ ID.value }},
        "location": {{ getRecord.data.location[0] }},
        "date": {
          "month": {{ getRecord.data.date[0].substring(5,7) }},
          "year": {{ getRecord.data.date[0].substring(0,4) }}
        },
        "supplier": {{ getRecord.data.supplier[0] }},
        "batch": {{ getRecord.data.batch[0] }},
        "status": {{ getRecord.data.status[0] }},
        "analysis": {{ getRecord.data.analysis[0] }}
    }
}

I have conducted extensive research, tested and retested the code, and have been unable to resolve the issue. I would appreciate any assistance in resolving this challenge.

AGD-Fersa commented 3 days ago

Final solution:

from office365.sharepoint.client_context import ClientContext
import base64

# Connection parameters and data
URL = 'https://your-sharepoint-site-url/'  # SharePoint site URL
URL_RELATIVE = 'your-relative-folder-path/'  # Relative folder path in SharePoint
USERNAME = 'your-email@example.com'  # SharePoint username
PASSWORD = 'your-password'  # SharePoint password
data = startTrigger.data  # Data received (file and metadata)
FILE = data['file']  # Base64 encoded file
META = data['meta']  # Metadata associated with the file

# Function to establish the SharePoint context
def get_sharepoint_context(url, user, password):
    ctx = ClientContext(url).with_user_credentials(user, password)
    return ctx

# Function to get the target folder in SharePoint
def get_target_folder(ctx, url_relative):
    target_folder = ctx.web.get_folder_by_server_relative_url(url_relative)
    return target_folder

# Function to upload a file to SharePoint with metadata
def upload_file_to_sharepoint(url, url_relative, user, password, file, meta):
    # Get the SharePoint context and target folder
    ctx = get_sharepoint_context(url, user, password)
    target_folder = get_target_folder(ctx, url_relative)

    # Decode the file and prepare its name
    file_content = base64.b64decode(file['base64Data'])
    file_name = file['name']

    # Upload the file to the specified folder
    uploaded_file = target_folder.upload_file(file_name, file_content).execute_query()

    # Assign metadata to the uploaded file
    file_item = uploaded_file.listItemAllFields
    for key in meta:
        value = meta[key]  # Get the metadata value
        file_item.set_property(key, value)  # Set the key-value pair in SharePoint

    # Save the changes in the metadata
    file_item.update()
    ctx.execute_query()

# Execute the file and metadata upload function
upload_file_to_sharepoint(URL, URL_RELATIVE, USERNAME, PASSWORD, FILE, META)
{
    "file": {
        "name": {{ fileInput1.file.name }},
        "base64Data": {{ fileInput1.file.uri.substring(fileInput1.file.uri.indexOf("base64,") + 7) }}
    }, "meta": {
        "NRecord": {{ ID.value }},
        "Date": {{ getRecord.data.date[0] }},
        "Location": {{ getRecord.data.location[0] }},
        "Supplier": {{ getRecord.data.supplier[0] }},
        "Batch": {{ getRecord.data.batch[0] }},
        "Status": {{ getRecord.data.status[0] }},
        "Analysis": {{ getRecord.data.analysis[0] }},
    }
}