formio / formio.js

JavaScript powered Forms with JSON Form Builder
https://formio.github.io/formio.js
MIT License
1.89k stars 1.06k forks source link

How to send additional parameters when saving files with File component? #3838

Closed mm-dsibinski closed 9 months ago

mm-dsibinski commented 3 years ago

Hi guys!

I'm using the File component (https://help.form.io/userguide/form-components/#file) in FormIO builder, so our users can upload files on their forms.

I implemented the ng-file-upload-compatible .NET server-side using the example from here.

When the file is dragged-and-dropped on the File component:

image

the "Add" method in my server controller is called. I noticed that, I assume FormIO, provides only the file content and file name parameter in the form data of the web request. It means that I don't have any other parameters available in this controller, like the name of the form or its ID (specific to our system).

The fact is that the files saved using this File component are having some unique ID assigned, so when the file is later downloaded it's downloaded with the name containing the unique ID:

image

That makes me wondering how to improve the files saving process with FormIO a bit. What I'd like to achieve is to be able to send some custom parameters to the files saving controller's Add method, for example the ID of the form object and the name of the form (we are saving the FormIO's form inside our own entity, which has name and ID). It would let me create a separate folder on our server for all files related only to this particular form.

Do you have any idea how this can be achieved? Thanks for your help :)

travist commented 3 years ago

This would require some extensive debugging to determine how this could be done. Maybe someone from the community has done this before and can lend a hand?

Sefriol commented 3 years ago

I am little unsure what is the desired goal. I would assume that all of this could be achieved with by adding custom FileService to the form itself?

By defining custom upload and download functions, I would assume that you can send and receive whatever parameters you want?

mm-dsibinski commented 3 years ago

@Sefriol I guess so. That would be the goal - to be able to modify the web request sent by the form to the custom files' provider (URL).

The desired goal would be to associate the files uploaded to the server with some object "wrapping" the form. In our case, we have an object called a Questionnaire and the form object (JSON definition of the FormIO form) is only a part of it. This Questionnaire has, for example, its own Id. I'd like to be able to group all files related to Questionnaire A in its own folder on the server. Thanks to that, it's easier to maintain. For instance, when the questionnaire object is deleted, we also delete all files uploaded from its form.

This is just an example, but I think there might be many use cases of that.

Sefriol commented 3 years ago

This is how we do this in VueJS:

<template>
  <div class="form-render">
    <Form
      ref="formio-form"
      :options="{
        fileService:storage
      }"
      :form="form"
      :submission="submission"
    />
  </div>
</template>
<script>

import { Form } from 'vue-formio'
import { downloadFile, uploadFile } from '@/api/files'
class FileService {
  uploadFile(storage, file, fileName, dir, evt, url, options, fileKey) {
    return new Promise((resolve, reject) => {
      console.log(storage, file, fileName, dir, evt, url, options, fileKey)
      const formJSON = {
        [fileKey]: file,
        name: fileName,
        dir
      }
      const fd = new FormData()

      for (const key in formJSON) {
        fd.append(key, formJSON[key])
      }
      uploadFile(fd)
        .then((res) => {
          return resolve({
            storage: 'url',
            name,
            url: res.data.url,
            size: file.size,
            type: file.type,
            data: res.data.uploadResponse.response
          })
        })
        .catch((err) => reject(err))
    })
  }
  async deleteFile(fileInfo) {
    console.log(fileInfo)
  }
  downloadFile(fileInfo, options) {
    return new Promise((resolve, reject) => {
      downloadFile(fileInfo.url).then(response => {
        var fileURL = window.URL.createObjectURL(new Blob([response.data]))
        var fileLink = document.createElement('a')

        fileLink.href = fileURL
        fileLink.setAttribute('download', fileInfo.originalName)
        document.body.appendChild(fileLink)

        fileLink.click()
        fileLink.remove()
        resolve()
      })
    })
  }
}

export default {
  name: 'FormRender',
  components: {
    Form
  },
  props: {
    storage: { type: Object, default: () => { return new FileService() } },
    form: { type: Object, default: () => { return { 'components': [] } } },
    submission: { type: Object, default: () => {} }
  }
}

Where /api files are just axios api wrappers. fileLink stuff is just some hackish things we use to resolve some unrelated UI problems. (And as you can probably see deleteFIle is just UI removal and does not actually send anything to backend)

mm-dsibinski commented 3 years ago

Thanks @Sefriol , that should work in React as well. I will check it in a few weeks when I get back to this issue and let you know the results :)

Tatsiana8 commented 9 months ago

Closing this thread as it is outdated. Please re-open if it is still relevant. Thank you for your contribution!