diff --git a/packages/server/src/routes/api/files.ts b/packages/server/src/routes/api/files.ts index ac3508d7c..f38165ca4 100644 --- a/packages/server/src/routes/api/files.ts +++ b/packages/server/src/routes/api/files.ts @@ -1,4 +1,4 @@ -import { ErrorNotFound, ErrorBadRequest } from '../../utils/errors'; +import { ErrorNotFound } from '../../utils/errors'; import { File } from '../../db'; import { bodyFields, formParse } from '../../utils/requestUtils'; import { SubPath, respondWithFileContent } from '../../utils/routeUtils'; @@ -57,8 +57,12 @@ router.put('api/files/:id/content', async (path: SubPath, ctx: AppContext) => { const fileModel = ctx.models.file({ userId: ctx.owner.id }); const fileId = path.id; const result = await formParse(ctx.req); - if (!result?.files?.file) throw new ErrorBadRequest('File data is missing'); - const buffer = await fs.readFile(result.files.file.path); + + // When an app PUTs an empty file, `result.files` will be an emtpy object + // (could be the way Formidable parses the data?), but we still need to + // process the file so we set its content to an empty buffer. + // https://github.com/laurent22/joplin/issues/4402 + const buffer = result?.files?.file ? await fs.readFile(result.files.file.path) : Buffer.alloc(0); const file: File = await fileModel.entityFromItemId(fileId, { mustExist: false }); file.content = buffer; diff --git a/packages/server/src/utils/requestUtils.ts b/packages/server/src/utils/requestUtils.ts index 81a303838..093a14942 100644 --- a/packages/server/src/utils/requestUtils.ts +++ b/packages/server/src/utils/requestUtils.ts @@ -1,4 +1,4 @@ -import { ErrorBadRequest, ErrorForbidden } from './errors'; +import { ErrorForbidden } from './errors'; import { AppContext } from './types'; const formidable = require('formidable'); @@ -36,11 +36,17 @@ export async function formParse(req: any): Promise { } export async function bodyFields(req: any): Promise { + // Formidable needs the content-type to be 'application/json' so on our side + // we explicitely set it to that. However save the previous value so that it + // can be restored. + let previousContentType = null; if (req.headers['content-type'] !== 'application/json') { - throw new ErrorBadRequest(`Unsupported Content-Type: "${req.headers['content-type']}". Expected: "application/json"`); + previousContentType = req.headers['content-type']; + req.headers['content-type'] = 'application/json'; } const form = await formParse(req); + if (previousContentType) req.headers['content-type'] = previousContentType; return form.fields; }