I have the following PUG template:
extends layout
block append head
link(type='stylesheet', href='/stylesheets/mystyle.css', rel='stylesheet')
block layout-content
#chat-app
#chat.has-text-centered
h1.title Welcome to #{title}
section.section.chat-container
.container
.columns
.box.column.is-full
h2.title Messages
.chat-messages.has-text-left
p#messages
form#chatForm(action='/api/gemini', method='post', enctype="multipart/form-data")
.field.has-addons
p.control.is-expanded
input(class='input', id='prompt', type='text', placeholder='How can I help?')
p.control
input(class='input', id='image', type='file')
p.control
input(class='button is-success', type='submit', value='Post')
script.
document.querySelector("#chatForm").addEventListener("submit", async (e) => {
e.preventDefault();
const prompt = document.querySelector("#prompt").value;
if (prompt.trim()) {
const response = await fetch('/api/myapi', {
method: 'POST',
headers: { 'Content-Type': 'multipart/form-data' },
body: JSON.stringify({, prompt:, prompt})
});
const data = await response.json();
document.querySelector("#messages").innerHTML += `<p><strong>You:</strong> ${prompt}</p>`;
document.querySelector("#messages").innerHTML += `<p><strong>Server:</strong> ${data.message}</p><br>`;
document.querySelector("#prompt").value = '';
}
});
And the server endpoint using multer
:
api.post('/myapi', upload.single('image'), function (req, res, next) { myapp.Handle(req, res, next); });
I can use either multer
or express-fileupload
. The concern here is the inline script in the PUG template. How can I send BOTH the text message and upload file in the same post request?
Use FormData to construct multipart request: append file to image key, and then append other data (JSON) to other key, and simply post form data as body, without content type headers, because it will automatically construct multipart request (file will be in req.file
, just make sure that file field name is the same on the server (.append('image'...
-> upload.single('image')
, and other data in req.body
in key/value form):
// get the image
const image = document.querySelector("#image").files[0];
const formData = new FormData();
// add the image -> access via `req.file`,
// also, field name must be the same on the server (image -> image)
formData.append('image', image);
// add other stuff -> acess via `req.body`
formData.append('prompt', JSON.stringify({prompt}));
// send data - it automatically constructs multipart request
// no need to manually add content type headers
const response = await fetch('/api/myapi', {
method: 'POST',
body: formData
});
or even shorter: you could simply pass the form element to FormData
, and it would be done automatically (all HTML form fields will be sent in key/value form):
note: for key/value pairs to work, you need to add corresponding name
attributes to the form
's input
elements
input(class='input', name='prompt', id='prompt', type='text', placeholder='How can I help?')
input(class='input', name='image', id='image', type='file')
etc.
const response = await fetch('/api/myapi', {
method: 'POST',
body: new FormData(e.target)
});