[JavaScript] Unable to Upload File to Parent

I have abandoned using Box with PHP because the support is abysmal. Since this is an internal only application anyways, I’m using JavaScript.

I can successfully download a file by searching for it using the /search?query=filename.ext&type=file endpoint. In the response, there are several entries, but I can find the specific file I’m looking for by using JavaScript’s find method checking the entry’s name and parent.id.

When I try to upload a file using the /files/content endpoint, I am getting a 404 from the response using this function:

async uploadFile(filename, rawFiles) {
	if (!this.#accessToken) {
		const errorMessage = 'Access Token is not set. Please authenticate first.'
		console.error(errorMessage);
		throw new Error(error);
	}

	const uploadUrl = 'https://api.box.com/2.0/files/content';
	const parentFolderId = '-redacted-';
	
	if (rawFiles?.length === 0) {
		const errorMessage = 'Please select a file.'
		console.error(errorMessage);
		throw new Error(error);
	}
	const file = rawFiles[0];
	const formData = new FormData();
	formData.append('attributes', JSON.stringify({
		name: filename,
		parent: { id: parentFolderId }
	}));
	formData.append('file', file);
  
	try {
		const response = await fetch(uploadUrl, {
			method: 'POST',
			headers: {
				'Authorization': `Bearer ${this.#accessToken}`
			},
			body: formData
		});
  
	  if (!response.ok) {
		throw new Error(`Server returned ${response.status}: ${response.statusText}`);
	  }
  
	  await response.json();
	} catch (error) {
		console.error('Upload error:', error);
		throw new Error(error);
	}
}

I don’t think this is a permission issue because with my search entries because I can confirm that value in the parent.id matches the value I’m passing in my parentFolderId variable. However, the only reason a 404 can be returned per the documentation is:

Returns an error if the parent folder does not exist or if the user is not authorized to access the parent folder.

Hi @dday9

I’m not sure if you should use https://api.box.com/2.0/files/content or https://upload.box.com/api/2.0/files/content for your POST query

Would it be possible to show us the full error message that you have for your 404 ?

@CodeBoxSeb - There is no error message being returned by the request, only a 404 response. These are the response headers:

Request URL: https://api.box.com/2.0/files/content
Request Method: POST
Status Code: 404 Not Found
Referrer Policy: strict-origin-when-cross-origin
Alt-Svc: h3=":443"; ma=2592000,h3-29=":443"; ma=2592000
Box-Request-Id: -redacted-
Content-Length: 0
Content-Type: text/html; charset=UTF-8
Date: Mon, 18 Mar 2024 13:27:56 GMT
Strict-Transport-Security: max-age=31536000
Via: 1.1 google
X-Envoy-Upstream-Service-Time: 16

Edit - Changing the URL to use the upload subdomain rather than the api subdomain errors as well, only the error returned is a CORS error:

Cross-Origin Resource Sharing error: PreflightMissingAllowOriginHeader

Edit 2 - So I converted my upload to PHP and made the call through PHP and it works. I don’t understand why doing it from the browser causes it to fail.

Does the client id/application you are making the call with have underlying permissions to upload to the folder? This may not be your user, as JWT and CCG auth types get a service account to represent them upon approval.

@smartoneinok - Yes, if you look at my second edit it works fine in PHP using the same client id, client secret, and enterprise id. It just isn’t working through JavaScript.

Alright! Let me dig in a bit further. Can I ask why you aren’t using an official Box SDK GitHub - box/sdks: SDKs, CLI and other tools for using Box Platform? It looks like you are just using native JS.

The follow code worked for me with a ccg app and my local url added to the cors listing in the box config. I used the global http-server package to host it at a local url. I also updated the folder id in the js to be one my app had access to.

HTML

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Box File Upload</title>
    <link rel="stylesheet" href="style.css">
    <link rel="icon" href="favicon.ico" type="image/x-icon">
</head>
<body>

<div class="container">
    <h2>Box File Upload</h2>
    <input type="text" id="tokenInput" placeholder="Enter your Box API token here">
    <input type="file" id="fileInput">
    <button id="uploadButton">Upload</button>
</div>

<script src="script.js"></script>
</body>
</html>

CSS

body {
    font-family: Arial, sans-serif;
    display: flex;
    justify-content: center;
    align-items: center;
    height: 100vh;
    margin: 0;
    background-color: #f4f4f4;
}

.container {
    background: #fff;
    padding: 20px;
    border-radius: 8px;
    box-shadow: 0 2px 4px rgba(0,0,0,0.1);
    text-align: center;
}

input[type="text"], input[type="file"] {
    margin: 10px 0;
    padding: 10px;
    width: 80%;
    border-radius: 5px;
    border: 1px solid #ddd;
}

button {
    padding: 10px 20px;
    background-color: #007bff;
    color: #fff;
    border: none;
    border-radius: 5px;
    cursor: pointer;
}

button:hover {
    background-color: #0056b3;
}

JS

document.addEventListener('DOMContentLoaded', function() {
    document.getElementById('uploadButton').addEventListener('click', function() {
        const token = document.getElementById('tokenInput').value.trim();
        const fileInput = document.getElementById('fileInput');
        const file = fileInput.files[0];

        if (!token) {
            alert('Please enter your Box API token.');
            return;
        }

        if (!file) {
            alert('Please select a file to upload.');
            return;
        }

        const formData = new FormData();
        formData.append('file', file);
        // The file's name is accessed directly from the file object
        formData.append('attributes', JSON.stringify({
            name: file.name,
            parent: { id: '0' } // Assuming you're uploading to the root folder or you can change it
        }));

        fetch('https://upload.box.com/api/2.0/files/content', {
            method: 'POST',
            headers: {
                Authorization: `Bearer ${token}`
            },
            body: formData
        })
        .then(response => {
            if (!response.ok) {
                return response.json().then(body => {
                    throw new Error(JSON.stringify(body)); // Convert the JSON body to string and throw it as an error
                });
            }
            return response.json();
        })
        .then(data => {
            console.log('Upload successful:', data);
            alert('File uploaded successfully.');
        })
        .catch(error => {
            console.error('Upload error:', error.message); // This contains the stringified JSON error
        
            let errorMessage = 'Upload failed. Please check the console for more details.';
            try {
                const errorObj = JSON.parse(error.message); // Parse the string back into an object
        
                if (errorObj && errorObj.message) {
                    console.error('Error message:', errorObj.message); // Log the error message
                    errorMessage += ` Error: ${errorObj.message}`;
                }
                if (errorObj && errorObj.code) {
                    console.error('Error code:', errorObj.code); // Log the error code
                }
            } catch (parseError) {
                console.error('Error parsing error message:', parseError);
            }
        
            alert(errorMessage);
        });        
    });
});