Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cannot upload PDF file to OneDrive, unclear API Error #869

Open
Mirciulica15 opened this issue Aug 22, 2024 · 3 comments
Open

Cannot upload PDF file to OneDrive, unclear API Error #869

Mirciulica15 opened this issue Aug 22, 2024 · 3 comments
Assignees
Labels

Comments

@Mirciulica15
Copy link

Mirciulica15 commented Aug 22, 2024

Describe the bug

I am trying to upload a PDF file to OneDrive, but I receive a 400 APIError with an Invalid Request.

Expected behavior

Successful upload of the file.

How to reproduce

files = [item.name for item in await self._get_onedrive_items(
    query_filter="file ne null and file/mimeType eq 'application/pdf'"
)]

folder_id_map = await self._get_folder_ids(source_folders)

for folder in source_folders:
    folder_path = os.path.join('documents', folder)
    if not os.path.exists(folder_path):
        continue

    for file_name in os.listdir(folder_path):
        if file_name in files:
            continue

        file_path = os.path.join(folder_path, file_name)

        with open(file_path, 'rb') as file:
            file_content = file.read()

        folder_id = folder_id_map.get(folder)
        if not folder_id:
            print(f"Folder ID for '{folder}' not found.")
            continue

        parent_reference = ItemReference(
            id=folder_id
        )

        drive_item = DriveItem(
            name=file_name,
            file=File(),
            parent_reference=parent_reference,
            content=file_content
        )

        try:
            response = await self._user_client.drives.by_drive_id(
                os.getenv('ONEDRIVE_DEV_DRIVE_ID')
            ).items.post(drive_item)

            print("Response", response)
        except Exception as e:
            print(f"Failed to upload file '{file_name}': {e}")

SDK Version

msgraph-core~=1.1.2 msgraph-sdk~=1.5.3

Latest version known to work for scenario above?

No response

Known Workarounds

No response

Debug output

Click to expand log ```

APIError
Code: 400
message: None
error: MainError(additional_data={}, code='invalidRequest', details=None, inner_error=InnerError(additional_data={}, client_request_id='13230f64-2c2e-4083-93c1-f7325291333c', date=DateTime(2024, 8, 22, 10, 12, 34, tzinfo=Timezone('UTC')), odata_type=None, request_id='f1815677-f808-46dd-af6d-7882803e975b'), message='Invalid request', target=None)

</details>


### Configuration

- OS: Windows
- architecture: AMD64

### Other information

The 400 APIError could be more robust than just "Invalid request"
@Mirciulica15 Mirciulica15 added status:waiting-for-triage An issue that is yet to be reviewed or assigned type:bug A broken experience labels Aug 22, 2024
@shemogumbe
Copy link
Collaborator

Hello @Mirciulica15 thanks for using the SDK and for reporting this,

How large is the file you want to upload, do you want to do it in chunks:

If so, use the LargeFileUploadTask from https://github.com/microsoftgraph/msgraph-sdk-python-core/

destination_path = "path/to/your_file.txt"
file_path = "path/to/your_file.txt"


async def upload_large_file():
    try:
        file = open(file_path, 'rb')
        uploadable_properties = DriveItemUploadableProperties(
            additional_data={'@microsoft.graph.conflictBehavior': 'replace'}
        )
        upload_session_request_body = CreateUploadSessionPostRequestBody(item=uploadable_properties)
        print(f"Uploadable Properties: {uploadable_properties.additional_data}")
        # can be used for normal drive uploads
        try:

            upload_session = await user_client.drives.by_drive_id(
                "b!WtUPKhiPm0a6Bj6Z_J97a53XIv_KoNNAkdLRUrSF06lh-qW6JpABSoW62oPNb03R"
            ).items.by_drive_item_id('root:/my_docs/test_upload.txt:'
                                     ).create_upload_session.post(upload_session_request_body)
            
        except APIError as ex:
            print(f"Error creating upload session: {ex}")

        # to be used for large file uploads
        large_file_upload_session = LargeFileUploadSession(
            upload_url=upload_session.upload_url,
            expiration_date_time=datetime.now() + timedelta(days=1),
            additional_data=upload_session.additional_data,
            is_cancelled=False,
            next_expected_ranges=upload_session.next_expected_ranges
        )

        # max_slice_size = 320 * 1024 - remove to use default
        task = LargeFileUploadTask(large_file_upload_session, user_client.request_adapter, file)
        total_length = os.path.getsize(file_path)
        
        # Upload the file
        # The callback
        def progress_callback(uploaded_byte_range: tuple[int, int]):
            print(f"Uploaded {uploaded_byte_range[0]} bytes of {total_length} bytes\n\n")

        try:
            upload_result = await task.upload(progress_callback)
            print(f"Upload complete {upload_result}")
        except APIError as ex:
            print(f"Error uploading: {ex.message} - {ex.response_status_code}")
    except APIError as e:
        print(f"Error: {e}")


asyncio.run(upload_large_file())

Optionally, on your snippet, you can use https://learn.microsoft.com/en-us/graph/api/driveitem-createuploadsession?view=graph-rest-1.0

@shemogumbe shemogumbe added status:waiting-for-author-feedback Issue that we've responded but needs author feedback to close and removed status:waiting-for-triage An issue that is yet to be reviewed or assigned labels Aug 22, 2024
@microsoft-github-policy-service microsoft-github-policy-service bot added Needs: Attention 👋 and removed status:waiting-for-author-feedback Issue that we've responded but needs author feedback to close labels Aug 22, 2024
@Mirciulica15
Copy link
Author

Thank you for the reply, Shem! I managed to do it using the large file upload method 😀

Do you know whether it is possible to place the files in a specific folder within the drive? I tried to do that with an ItemReference, but it did not work out.

I would like to make a PR with my implementation in the examples directory later if that's ok, in case other people have the same use case and need help.

@TwE3d
Copy link

TwE3d commented Nov 17, 2024

For anyone stumbling across this issue.
You can specifiy the folder in the following line:

upload_session = await user_client.drives.by_drive_id(
                "b!WtUPKhiPm0a6Bj6Z_J97a53XIv_KoNNAkdLRUrSF06lh-qW6JpABSoW62oPNb03R"
            ).items.by_drive_item_id('root:/path/to/folder/test_upload.txt:'
                                     ).create_upload_session.post(upload_session_request_body)

It will create the folder, if necessary.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants