How to programmatically submit a form on Drupal with an attached file
For TechAbantu, we recently had the need to handle a full form submission programmatically, including attaching files. This is to support the API for the data archive solution on blockchain: the solution supports a web-based interface, but is primary intended for customers to integrate their information systems through the API. The natural solution is to support HTTP multipart forms through the API.
Drupal supports multipart forms and file uploads both very well. There is a lot of information in the blogs about how to handle each of these. But when we tried to combine into one form submit, the file kept being lost. The blogs suggested doing separate POST requests - one for the form, and the other for the file. But that just seems ugly and seems to defeat the purpose of having a multipart form.
So we put on our safari hats and dove into the jungle of Drupal form submission and file uploads. Here is what we learned:
- We had to set up both of the "input" and "values" fields of $form_state, and they require different structures: where $file is the file object,
$form_state['values']['my_field'] = (array)$file; $form_state['input']['my_field'] = $file->fid;
Even though "values" gets rewritten, for files the field is accessed earlier.
- Even through the file is written into the tmp:// directory, we still had to use file_save_data to save it to a permanent location.
- And now the interesting part that took the better part of the day to figure out: if the file is private, it will not be saved with the form. A file saved with the form needs to be:
- Permanent (status = 1);
- Used (file_usage_add);
- Public. To facilitate saving a private file, we had to add the fid to $_SESSION.
If the form validation or submission fails, we delete the file at that point after removing the file usage.
Now the submission works with one HTTP POST request, using multipart form, directly into our Drupal form.
We hope that you find this to be useful for your Drupal work!