I was getting this problem with Axios via JavaScript because the content-type header was multipart-form-data but the boundary was missing.
Based on my research, a good way to handle it is to allow Axios to auto-detect the content type and set the headers correctly itself.
Here is an idea for how to accomplish this:
const formDataWithFiles = hasFiles ? new FormData() : undefined;
if (formDataWithFiles) {
// axios will automatically set the content-type to multipart/form-data if the
// data param is a FormData object
// otherwise, it will use application/json
// (study the Dev Tools > Network tab > XHR tab headers)
Object.keys(modifiedFields)
.forEach(field => formDataWithFiles.append(field, modifiedFields[field]));
}
const { data } = await axios({
method,
url: actionUrl,
data: hasFiles ? formDataWithFiles : modifiedFields,
headers: {
...axios.defaults.headers,
...headers,
},
});
return data;
The above code is in a generic handleSubmit function that can be called from anywhere in the client-side.
Here is the function signature:
const { data } = await this.submitForm({
actionUrl: this.actionUrl,
method: this.method,
modifiedFields: {
...this.modifiedUser,
},
hasFiles: true,
});
In the above code, there are two use cases. The first is the default case, where a normal payload is sent via a flat object. The second is the case when the form has files and you want multipart/form-data
. In this case, we use the FormData
Object as a vessel to instruct Axios to auto-detect the necessary headers and set the correct boundary.
If you do not specify the headers correctly, it is possible to receive an empty $request->all()
Array in Laravel, or perhaps any server such as node.js.
The short answer to my answer is to use the FormData
Object because it contains more information than a plain-old-JavaScript-object. With it, you can also access:
const formData = new FormData();
console.log('boundary:', formData._boundary);
As my annotation above hints towards, use the Dev Tools > Network tab > XHR tab to examine your request headers and make sure you have content-type application/json
or application/x-www-form-urlencoded
for regular form submits and multipart/form-data'
if you are uploading a file.
«dependencies»: {
«@nestjs/common»: «^7.0.9»,
«@nestjs/core»: «^7.0.9»,
«@nestjs/jwt»: «^7.0.0»,
«@nestjs/passport»: «^7.0.0»,
«@nestjs/platform-express»: «^7.0.9»,
«@nestjs/schedule»: «^0.3.1»,
«@nestjs/typeorm»: «^7.0.0»,
«@sentry/node»: «^5.15.5»,
«aws-sdk»: «^2.674.0»,
«axios»: «^0.19.2»,
«bcryptjs»: «^2.4.3»,
«body-parser»: «^1.19.0»,
«class-transformer»: «^0.2.3»,
«class-validator»: «^0.12.2»,
«dotenv-safe»: «^8.2.0»,
«express-rate-limit»: «^5.1.3»,
«faker»: «^4.1.0»,
«handlebars»: «^4.7.6»,
«ioredis»: «^4.16.3»,
«module-alias»: «^2.2.2»,
«ms»: «^2.1.2»,
«nestjs-s3»: «^1.0.1»,
«nodemailer»: «^6.4.6»,
«passport»: «^0.4.1»,
«passport-jwt»: «^4.0.0»,
«pg»: «^8.1.0»,
«pino»: «^6.2.1»,
«rate-limit-redis»: «^1.7.0»,
«reflect-metadata»: «^0.1.13»,
«rxjs»: «^6.5.5»,
«source-map-support»: «^0.5.19»,
«typeorm»: «^0.2.24»,
«uuid»: «^8.0.0»
},
«devDependencies»: {
«@nestjs/cli»: «^7.1.5»,
«@nestjs/schematics»: «^7.0.0»,
«@types/bcryptjs»: «^2.4.2»,
«@types/body-parser»: «^1.19.0»,
«@types/dotenv-safe»: «^8.1.0»,
«@types/express»: «^4.17.6»,
«@types/express-rate-limit»: «^5.0.0»,
«@types/faker»: «^4.1.11»,
«@types/ioredis»: «^4.16.1»,
«@types/module-alias»: «^2.0.0»,
«@types/ms»: «^0.7.31»,
«@types/node»: «^12.12.38»,
«@types/nodemailer»: «^6.4.0»,
«@types/passport-jwt»: «^3.0.3»,
«@types/pino»: «^6.0.1»,
«@types/rate-limit-redis»: «^1.7.1»,
«@types/source-map-support»: «^0.5.1»,
«@types/uuid»: «^7.0.3»,
«newman»: «^5.0.0»,
«pino-pretty»: «^4.0.0»,
«pm2»: «^4.4.0»,
«prettier»: «^2.0.5»,
«ts-loader»: «^7.0.4»,
«ts-node»: «^8.10.1»,
«tsconfig-paths»: «^3.9.0»,
«tslint»: «^6.1.2»,
«tslint-config-prettier»: «^1.18.0»,
«tslint-config-standard»: «^9.0.0»,
«tslint-sonarts»: «^1.9.0»,
«typeorm-seeding»: «^1.6.1»,
«typescript»: «^3.8.3»
},
«optionalDependencies»: {
«husky»: «^4.2.1»,
«lint-staged»: «^10.0.7»
}
При отправке изображения получаю ошибку Error: Multipart: Boundary not found
На фронтенде отправка выглядит следующим образом
1. View
<input type="file" id="file" (change)="handleFileInput($event.target.files)">
2. Component
handleFileInput(files: FileList) {
this.fileToUpload = files.item(0);
this.userService.postFile(this.fileToUpload)
.subscribe(
response => {
console.log(response);
},
error => {
console.log('error', error);
}
);
}
3. Service
postFile(fileToUpload: File): Observable<any> {
const formData: FormData = new FormData();
formData.append('fileKey', fileToUpload, fileToUpload.name);
return this.apiService.post(this.apiConfig.photo, formData, true);
}
Вот что я вижу во вкладке Network
General
Request URL: http://localhost:8000/api/photo
Request Method: POST
Status Code: 500 Internal Server Error
Remote Address: [::1]:8000
Referrer Policy: no-referrer-when-downgrade
<b>Response Headers</b>
Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept, Authorization
Access-Control-Allow-Methods: GET,PUT,POST,PATCH,DELETE
Access-Control-Allow-Origin: *
Connection: keep-alive
Content-Length: 1864
Content-Security-Policy: default-src 'self'
Content-Type: text/html; charset=utf-8
Date: Mon, 07 May 2018 12:56:18 GMT
X-Content-Type-Options: nosniff
X-Powered-By: Express
Request Headers
Accept: application/json, text/plain, */*
Accept-Encoding: gzip, deflate, br
Authorization: xxx
Cache-Control: no-cache
Connection: keep-alive
Content-Length: 2
Content-Type: multipart/form-data
Host: localhost:8000
Origin: http://localhost:4200
Pragma: no-cache
Referer: http://localhost:4200/user/test
Request payload
{}
No properties
На бекенде NodeJS использую multer
const multer = require("multer");
var eventStorage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, "./storage");
},
filename: function (req, file, cb) {
var filename = "";
var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
for (var i = 0; i < 20; i++) {
filename += possible.charAt(Math.floor(Math.random() * possible.length));
}
cb(null, filename + '.' + mime.extension(file.mimetype));
}
});
var uploadEvent = multer({
storage: eventStorage
});
router.post('/api/photo', auth.ensureAuthenticated, uploadEvent.any() , function (request, response) {
console.log(request.files);
handler.success(response, '', request.files);
});
Я не могу понять, где косяк — на фронтенде или бекенде?
Смущает пустой объект в Request payload, по идее там должен быть файл в виде хеша или типа того?
Today, when the front-end uses Vue to transfer files with formdata, the background node reports an error
Multipart: Boundary not found multer
It is found in the front console that the file file transmitted by the interface is null {}, because it is set when Axios requests
the Header of Content-Type is multipart/form-data
It is found that there is no boundary parameter later, except multipart/form data
multipart/form-data;boundary :****************
There should also be a series of boundary parameters. First of all, do not splice the contents behind the boundary, otherwise you need to reconfigure all the content parameters yourself.
later, it is found that it is the reason for the request method, and you need to use ajax to make the request.
the solution is as follows
// upload the files
export function reqUploadImg(file, user_id) {
return axios({
url: '/uploadImg',
method: 'POST',
Headers: {
"content-type": "multipart/form-data",
},
data: file,
params: { user_id }
})
}
This problem can be solved. It will automatically splice a string later, or “content type”: “multipart/form-data” in “multipart/form-data” will also be spliced automatically if it is set to fasle, but after personal experiments, it is found that the types have changed into JSON format, which is automatically generated by Axios source code
Read More:
Передняя часть использует запрос на размещение axios для загрузки файлов, а сервер Node сообщает об ошибке.Error: Multipart: Boundary not found
.
server is running . http://:::3000
Error: Multipart: Boundary not found
at new Multipart (/home/w/my/project-exercise/FileServer/node_modules/busboy/lib/types/multipart.js:58:11)
at Multipart (/home/w/my/project-exercise/FileServer/node_modules/busboy/lib/types/multipart.js:26:12)
at Busboy.parseHeaders (/home/w/my/project-exercise/FileServer/node_modules/busboy/lib/main.js:71:22)
at new Busboy (/home/w/my/project-exercise/FileServer/node_modules/busboy/lib/main.js:22:10)
at multerMiddleware (/home/w/my/project-exercise/FileServer/node_modules/multer/lib/make-middleware.js:33:16)
at Layer.handle [as handle_request] (/home/w/my/project-exercise/FileServer/node_modules/express/lib/router/layer.js:95:5)
at trim_prefix (/home/w/my/project-exercise/FileServer/node_modules/express/lib/router/index.js:317:13)
at /home/w/my/project-exercise/FileServer/node_modules/express/lib/router/index.js:284:7
at Function.process_params (/home/w/my/project-exercise/FileServer/node_modules/express/lib/router/index.js:335:12)
at next (/home/w/my/project-exercise/FileServer/node_modules/express/lib/router/index.js:275:10)
Сервер Node использует другие методы для загрузки теста и прохождения, внешний код загрузки vue выглядит следующим образом
let file = document.getElementById('file').files[0]
console.log(file)
this.axios.put('http://192.168.xxx.xxx:3000/file_upload_put', file, {
headers: {
'Content-Type': 'multipart/form-data'
},
transformRequest: [function (data) {
return data
}],
onUploadProgress: progressEvent => {
let complete = (progressEvent.loaded / progressEvent.total * 100 | 0) + '%'
console.log('complete: ', complete)
}
})
.then((response) => {
if (response.status === 200) {
console.log('success upload')
}
})
Решение
Не доставленformData
Объект заставил сервер сообщить об ошибке, и запрос на размещение включилfile
Объект добавлен вformData
, Пройти напрямуюformData
Объект можно загрузить в обычном режиме
let file = document.getElementById('file').files[0]
console.log(file)
let formData = new FormData()
formData.append('image', file)
this.axios.put('http://192.168.xxx.xxx:3000/file_upload_put', formData, {
headers: {
'Content-Type': 'multipart/form-data'
},
transformRequest: [function (data) {
return data
}],
onUploadProgress: progressEvent => {
let complete = (progressEvent.loaded / progressEvent.total * 100 | 0) + '%'
console.log('complete: ', complete)
}
})
.then((response) => {
if (response.status === 200) {
console.log('success upload')
}
})
I was getting this problem with Axios via JavaScript because the content-type header was multipart-form-data but the boundary was missing.
Based on my research, a good way to handle it is to allow Axios to auto-detect the content type and set the headers correctly itself.
Here is an idea for how to accomplish this:
const formDataWithFiles = hasFiles ? new FormData() : undefined;
if (formDataWithFiles) {
// axios will automatically set the content-type to multipart/form-data if the
// data param is a FormData object
// otherwise, it will use application/json
// (study the Dev Tools > Network tab > XHR tab headers)
Object.keys(modifiedFields)
.forEach(field => formDataWithFiles.append(field, modifiedFields[field]));
}
const { data } = await axios({
method,
url: actionUrl,
data: hasFiles ? formDataWithFiles : modifiedFields,
headers: {
...axios.defaults.headers,
...headers,
},
});
return data;
The above code is in a generic handleSubmit function that can be called from anywhere in the client-side.
Here is the function signature:
const { data } = await this.submitForm({
actionUrl: this.actionUrl,
method: this.method,
modifiedFields: {
...this.modifiedUser,
},
hasFiles: true,
});
In the above code, there are two use cases. The first is the default case, where a normal payload is sent via a flat object. The second is the case when the form has files and you want multipart/form-data
. In this case, we use the FormData
Object as a vessel to instruct Axios to auto-detect the necessary headers and set the correct boundary.
If you do not specify the headers correctly, it is possible to receive an empty $request->all()
Array in Laravel, or perhaps any server such as node.js.
The short answer to my answer is to use the FormData
Object because it contains more information than a plain-old-JavaScript-object. With it, you can also access:
const formData = new FormData();
console.log('boundary:', formData._boundary);
As my annotation above hints towards, use the Dev Tools > Network tab > XHR tab to examine your request headers and make sure you have content-type application/json
or application/x-www-form-urlencoded
for regular form submits and multipart/form-data'
if you are uploading a file.
By default axios do not attach boundary to content type header. You have to do it manually:
axios.post(`${this.baseUrl}/${path}`, formData, {
headers: {
'Content-Type': `multipart/form-data; boundary=${formData.getBoundary()}`,
},
})
It is especially important if you talking to spring server.
In other case you will see exception:
org.apache.tomcat.util.http.fileupload.FileUploadException: the request was rejected because no multipart boundary was found
I was struggling with this issue of multipart boundary not found with fetch api calling to a nestjs server. What I tried was to remove the
'Content-Type': 'multipart/form-data'
,
headers so that Fetch api automatically set the headers and it worked. Try it out
You can do this …
Instantiate a new FormData
instance.
const config = { headers: { 'Content-Type': 'multipart/form-data' } };
let fd = new FormData();
fd.append('file',files[0])
return axios.post("http://localhost:5000/upload", fd, config)
Usingconcat
and concat-stream
const concat = require("concat-stream")
const fd = new FormData()
fd.append("hello", "world")
fd.append("file", fs.createReadStream(file))
fd.pipe(concat(data => {
axios.post("/hello", data, {
headers: fd.getHeaders()
})
}))
Using promise
const promise = new Promise((resolve) => {
const fd = new FormData();
fd.append("hello", "world");
fd.append("file", fs.createReadStream(binaryFile));
fd.pipe(concat({ encoding: 'buffer' }, data => resolve({ data, headers: fd.getHeaders() })));
});
promise.then(({ data, headers }) => axios.post('/hello', data, { headers }));
I hope I’ve been useful!
References:
- github.com — Can’t get a .post with Content-Type…
- github.com — Better solution using axios, form-data, fs
- https://stackoverflow.com/a/47630754/3332734