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

BlobClient and Excel files #161

Open
nawgz opened this issue May 6, 2019 · 1 comment
Open

BlobClient and Excel files #161

nawgz opened this issue May 6, 2019 · 1 comment

Comments

@nawgz
Copy link
Contributor

nawgz commented May 6, 2019

I am running into a bit of a perplexing situation.

I parse and generate excel files as part of the model creation loop for my end users, so accordingly I take file uploads and pass them to my plugins thru the BlobClient.

In my frontend, I have a React class FileInput that uploads files to the BlobClient. However, I do not quite understand the interactions.

I have two cases:
1: The first version works correctly in all regards, except it caches the files with the same name. This is a problem for me, because the user can be editing their file and re-upload it, and the changes are not visible to the plugin as it sees the first uploaded version. This version creates an artifact using the createArtifact(fileName) function,
2: The second case directly uses putFile, and has a crippling issue: I cannot recover the information about the file (primarily, its name). This is inconvenient when the user uploads a file, and then they reopen the plugin config, and while the result is cached and ready to be ran without further interaction, I cannot display the filename, so the user thinks they must re-upload.

Here is a code sample of my component which interfaces with the BlobClient, and the two cases:

class FileInput extends React.Component {
  @observable $state = {
    uploaded: false,
    fileName: '',
    hash: null,
    label: 'Choose a file',
    artifact: null,
  }

  constructor(props) {
    super(props);

    const { BlobClient } = this.props.GME.classes;
    const { logger } = this.props;

    this.blobClient = new BlobClient({
      logger: logger.fork('FileInput'),
      uploadProgressHandler: this.uploadProgressHandler,
    });
  }

  uploadProgressHandler(fName, e) {
    // consumes e.percent if it's a number
  }

  componentDidMount() {
    if (this.props.fileHash) {
      this.recoverFileFromHash(this.props.fileHash);   
    }
  }

  onChange() {
    if (typeof this.props.onChange === 'function')
      this.props.onChange(this.$state.hash);
  }

  @action
  reset() {
    this.$state.uploaded = false;
    this.$state.fileName = false;
    this.$state.hash = null;
    this.$state.label = 'Choose a file';
    this.$state.artifact = null;
  }

  @action
  finishedUpload(metadata) {
    this.$state.uploaded = true;
    this.$state.fileName = metadata.name;
    this.$state.hash = fileHash;
    this.$state.label = 'Change file';
  }

  // upload the file if they upload
  // clear the current file if they cancel
  @action
  onFileChanged(event) {
    event.stopPropagation();
    event.preventDefault();

    const files = event.target.files || event.dataTransfer.files;

    if (files && files.length === 1) {
      this.upload(files[0]);
    } else {
      this.reset();
    }
  }

  recoverFileFromHash(fileHash) {
    this.blobClient.getMetadata(fileHash)
    .then((metadata) => {
      this.finishedUpload(metadata);
    })
    .catch((err) => {
      this.reset();
      this.onChange();
    });
  }


  /* FIRST VERSION, works correctly except caching */
  @action
  upload() {
    this.reset();

    // two files with the same `name` property will both return whatever file was uploaded first
    const fileArtifact = this.blobClient.createArtifact(this.$state.fileName);

    fileArtifact.addFileAsSoftLink(this.$state.fileName, file)
    .then((metadataHash) => {
      return (
        fileArtifact.save()
        .then(() => this.blobClient.getMetadata(metadataHash))
        .then((metadata) => this.finishedUpload(metadata))
      );
    })
    .catch((err) => {
      console.log('Errors not handled?');
    });
  }

  /* SECOND VERSION, doesn't hold file name or anything? */
  @action
  upload() {
    this.blobClient.putFile(this.$state.filename, file)
    .then((metadataHash) => {
      // first way you might expect to get metadata:
      this.blobClient.getArtifact(metadataHash)
      /* fails like:
      Errors not handled? Error: not supported contentType {
          "name": "undefined",
          "size": 26360,
          "mime": "application/octet-stream",
          "isPublic": false,
          "tags": [],
          "content": "4ca68d033dc2a590409baf2058ee70c11437aa45",
          "contentType": "object",
          "lastModified": "2019-05-06T23:48:19.329Z"
      }
      at webgme.classes.build.js:46378
      at webgme.classes.build.js:5341
      */

      // second way you might expect to get metadata:
      this.blobClient.getMetadata(metadataHash)
      /* succeeds, but returns a useless object, like:
      {
        "name": "undefined",
        "size": 26360,
        "mime": "application/octet-stream",
        "isPublic": false,
        "tags": [],
        "content": "4ca68d033dc2a590409baf2058ee70c11437aa45",
        "contentType": "object",
        "lastModified": "2019-05-06T23:48:19.329Z"
      }
      */
    });
  }
}

So, for my two cases, my questions are:

  1. How to avoid this caching behavior of artifacts based on name? It occurs to me now that this artifact name could be a random UUID, which would possibly bust the caching. I'll try that.
  2. Why does the BlobClient "not support" some filetypes when it still can handle their data correctly? I understand that you may be trying to avoid situations where files are passed around with incorrect content-type headers or something, but at least preserving the filename would likely make the BlobClient more versatile.

Thanks

@nawgz
Copy link
Contributor Author

nawgz commented May 7, 2019

Okay, so I can confirm that I can avoid the problem I was having with artifacts with the same filename using cached versions by creating a random numeric ID instead of using the filename in the createArtifact call, which in some way solves my initial problem.

However, question 2 still stands out for me a bit, so I'll leave this open for a little dialogue around how I'm using the BlobClient and how you would anticipate it being used.

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

No branches or pull requests

1 participant