Attaching a file to a page using the JavaScript API

This topic is a follow up to Attach Image to confluence page. The following code shows how to attach a file using the JavaScript API.

import React, { PureComponent } from 'react';

/* global AP */

export default class RequestApiFileUploadExample extends PureComponent {

  constructor(props) {
    super(props);
    this.state = {
      selectedFile: undefined,
      fileUploadStatus: 'Not uploaded',
    }
  }

  onFileInputChange = (e) => {
    this.setState({
      selectedFile: e.target.files[0]
    })
  }

  onFormSubmit = (e) => {
    e.preventDefault() // Stop form submission
    this.uploadFile(this.state.selectedFile)
      .then((response)=>{
        console.log(response.data);
        this.setState({
          fileUploadStatus: 'File uploaded'
        })
      })
      .catch((error) => {
        console.log('Error:', error);
        this.setState({
          fileUploadStatus: 'Error: ' + error.toString()
        });
      });
    return false;
  }

  uploadFile = (fileToUpload) => {
    var payload = {
      file: fileToUpload,
      comment: 'test attachment',
      minorEdit: 'true'
    };
    return new Promise((resolve, reject) => {
      AP.context.getContext((context) => {
        console.log('Received context:', context);
        AP.request({
          url: `/rest/api/content/${context.confluence.content.id}/child/attachment`,
          type: 'POST',
          data: payload,
          contentType: 'multipart/form-data',
          headers: {
            'Accept': 'application/json'
          },
          success: function (data) {
            console.log('File upload success:');
            console.log(data);
            resolve(data);
          },
          error: function(jqXHR, textStatus, errorThrown) {
            console.log('File upload error:');
            console.log(arguments);
            reject(errorThrown);
          }
        });    
      });
    });
  }

  renderUploadFileForm = () => {
    return (
      <form onSubmit={this.onFormSubmit}>
        <input type="file" onChange={this.onFileInputChange} />
        <button type="submit" disabled={!this.state.selectedFile}>Upload</button>
      </form>
    );
  };

  render() {
    return (
      <div>
        <h3>File upload example</h3>
        {this.renderUploadFileForm()}
        Upload status: {this.state.fileUploadStatus}
      </div>
    );
  }
}

This code works when running in Firefox, but Chrome produces an error net::ERR_ACCESS_DENIED.

2 Likes

FYI, we are investigating the Chrome issue. This may be related: https://support.google.com/chrome/thread/49467697

Dear @dmorrow,
Thank you very much for your help!
I try to create new file object and it can work:

var uploadFile = new File([file], newFileName, {
type: file.type,
});

var uploadData = {comment: “test attachment”, file: uploadFile};

1 Like

Thanks @elitesoftsp,

Incorporating your snippet results in the following which works around the Chrome bug:

import React, { PureComponent } from 'react';

/* global AP */

export default class RequestApiFileUploadExample extends PureComponent {

  constructor(props) {
    super(props);
    this.state = {
      selectedFile: undefined,
      fileUploadStatus: 'Not uploaded',
    }
  }

  onFileInputChange = (e) => {
    this.setState({
      selectedFile: e.target.files[0]
    })
  }

  onFormSubmit = (e) => {
    e.preventDefault() // Stop form submission
    // Construct a new File object to avoid Chrome crashing.
    const fileToUpload = new File([this.state.selectedFile], this.state.selectedFile.name, {
      type: this.state.selectedFile.type,
    });
    this.uploadFile(fileToUpload)
      .then((response)=>{
        console.log(response.data);
        this.setState({
          fileUploadStatus: 'File uploaded'
        })
      })
      .catch((error) => {
        console.log('Error:', error);
        this.setState({
          fileUploadStatus: 'Error: ' + error.toString()
        });
      });
    return false;
  }

  uploadFile = (fileToUpload) => {
    var payload = {
      file: fileToUpload,
      comment: 'test attachment',
      minorEdit: 'true'
    };
    return new Promise((resolve, reject) => {
      AP.context.getContext((context) => {
        console.log('Received context:', context);
        AP.request({
          url: `/rest/api/content/${context.confluence.content.id}/child/attachment`,
          type: 'POST',
          data: payload,
          contentType: 'multipart/form-data',
          headers: {
            'Accept': 'application/json'
          },
          success: function (data) {
            console.log('File upload success:');
            console.log(data);
            resolve(data);
          },
          error: function(jqXHR, textStatus, errorThrown) {
            console.log('File upload error:');
            console.log(arguments);
            reject(errorThrown);
          }
        });    
      });
    });
  }

  renderUploadFileForm = () => {
    return (
      <form onSubmit={this.onFormSubmit}>
        <input type="file" onChange={this.onFileInputChange} />
        <button type="submit" disabled={!this.state.selectedFile}>Upload</button>
      </form>
    );
  };

  render() {
    return (
      <div>
        <h3>File upload example</h3>
        {this.renderUploadFileForm()}
        Upload status: {this.state.fileUploadStatus}
      </div>
    );
  }
}

Regards,
Dugald

1 Like