feat: allows upload through Local API

This commit is contained in:
James
2021-02-05 16:09:53 -05:00
parent fd01715b5b
commit 1a590287ea
13 changed files with 129 additions and 13 deletions

View File

@@ -18,6 +18,7 @@ import { AfterChangeHook, BeforeOperationHook, BeforeValidateHook, Collection }
import { PayloadRequest } from '../../express/types';
import { Document } from '../../types';
import { Payload } from '../..';
import saveBufferToFile from '../../uploads/saveBufferToFile';
export type Arguments = {
collection: Collection
@@ -152,7 +153,7 @@ async function create(this: Payload, incomingArgs: Arguments): Promise<Document>
const fsSafeName = await getSafeFilename(staticPath, file.name);
try {
await file.mv(`${staticPath}/${fsSafeName}`);
await saveBufferToFile(file.data, `${staticPath}/${fsSafeName}`);
if (isImage(file.mimetype)) {
const dimensions = await getImageSize(`${staticPath}/${fsSafeName}`);

View File

@@ -1,5 +1,5 @@
import { UploadedFile } from 'express-fileupload';
import { Document } from '../../../types';
import getFileByPath from '../../../uploads/getFileByPath';
export type Options = {
collection: string
@@ -11,9 +11,8 @@ export type Options = {
overrideAccess?: boolean
disableVerificationEmail?: boolean
showHiddenFields?: boolean
file?: UploadedFile
filePath?: string
}
export default async function create(options: Options): Promise<Document> {
const {
collection: collectionSlug,
@@ -25,7 +24,7 @@ export default async function create(options: Options): Promise<Document> {
overrideAccess = true,
disableVerificationEmail,
showHiddenFields,
file,
filePath,
} = options;
const collection = this.collections[collectionSlug];
@@ -43,7 +42,7 @@ export default async function create(options: Options): Promise<Document> {
locale,
fallbackLocale,
payload: this,
file,
file: getFileByPath(filePath),
},
});
}

View File

@@ -0,0 +1,50 @@
import path from 'path';
import payload from '../../..';
let createdMediaID;
payload.init({
secret: 'SECRET_KEY',
mongoURL: 'mongodb://localhost/payload',
local: true,
});
describe('Collections - Local', () => {
describe('Create', () => {
it('should allow an upload-enabled file to be created and uploaded', async () => {
const alt = 'Alt Text Here';
const result = await payload.create({
collection: 'media',
data: {
alt,
},
filePath: path.resolve(__dirname, '../../../admin/assets/images/generic-block-image.svg'),
});
expect(result.id).not.toBeNull();
expect(result.alt).toStrictEqual(alt);
expect(result.filename).toStrictEqual('generic-block-image.svg');
createdMediaID = result.id;
});
});
describe('Update', () => {
it('should allow an upload-enabled file to be re-uploaded and alt-text to be changed.', async () => {
const newAltText = 'New Alt Text Here';
const result = await payload.update({
collection: 'media',
id: createdMediaID,
data: {
alt: newAltText,
},
filePath: path.resolve(__dirname, '../../../admin/assets/images/og-image.png'),
});
expect(result.alt).toStrictEqual(newAltText);
expect(result.sizes.mobile.width).toStrictEqual(320);
expect(result.width).toStrictEqual(640);
});
});
});

View File

@@ -1,5 +1,5 @@
import { UploadedFile } from 'express-fileupload';
import { Document } from '../../../types';
import getFileByPath from '../../../uploads/getFileByPath';
export type Options = {
collection: string
@@ -11,7 +11,7 @@ export type Options = {
user?: Document
overrideAccess?: boolean
showHiddenFields?: boolean
file?: UploadedFile
filePath?: string
}
export default async function update(options: Options): Promise<Document> {
@@ -25,7 +25,7 @@ export default async function update(options: Options): Promise<Document> {
user,
overrideAccess = true,
showHiddenFields,
file,
filePath,
} = options;
const collection = this.collections[collectionSlug];
@@ -43,7 +43,7 @@ export default async function update(options: Options): Promise<Document> {
locale,
fallbackLocale,
payload: this,
file,
file: getFileByPath(filePath),
},
};

View File

@@ -18,6 +18,7 @@ import { FileData } from '../../uploads/types';
import { PayloadRequest } from '../../express/types';
import { hasWhereAccessResult, UserDocument } from '../../auth/types';
import saveBufferToFile from '../../uploads/saveBufferToFile';
export type Arguments = {
collection: Collection
@@ -198,7 +199,7 @@ async function update(incomingArgs: Arguments): Promise<Document> {
const fsSafeName = await getSafeFilename(staticPath, file.name);
try {
await file.mv(`${staticPath}/${fsSafeName}`);
await saveBufferToFile(file.data, `${staticPath}/${fsSafeName}`);
fileData.filename = fsSafeName;
fileData.filesize = file.size;

View File

@@ -0,0 +1,22 @@
import fs from 'fs';
import mime from 'mime';
import { File } from './types';
const getFileByPath = (filePath: string): File => {
if (typeof filePath === 'string') {
const data = fs.readFileSync(filePath);
const mimetype = mime.getType(filePath);
const name = filePath.split('/').pop();
return {
data,
mimetype,
name,
};
}
return undefined;
};
export default getFileByPath;

View File

@@ -0,0 +1,21 @@
import { Readable } from 'stream';
import fs from 'fs';
/**
* Save buffer data to a file.
* @param {Buffer} buffer - buffer to save to a file.
* @param {string} filePath - path to a file.
*/
const saveBufferToFile = async (buffer: Buffer, filePath: string): Promise<void> => {
// Setup readable stream from buffer.
let streamData = buffer;
const readStream = new Readable();
readStream._read = () => {
readStream.push(streamData);
streamData = null;
};
// Setup file system writable stream.
return fs.writeFileSync(filePath, buffer);
};
export default saveBufferToFile;

View File

@@ -41,3 +41,9 @@ export type Upload = {
staticDir: string
adminThumbnail?: string
}
export type File = {
data: Buffer
mimetype: string
name: string
}