feat: allows upload through Local API
This commit is contained in:
@@ -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}`);
|
||||
|
||||
@@ -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),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
50
src/collections/operations/local/local.spec.js
Normal file
50
src/collections/operations/local/local.spec.js
Normal 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);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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),
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
22
src/uploads/getFileByPath.ts
Normal file
22
src/uploads/getFileByPath.ts
Normal 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;
|
||||
21
src/uploads/saveBufferToFile.ts
Normal file
21
src/uploads/saveBufferToFile.ts
Normal 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;
|
||||
@@ -41,3 +41,9 @@ export type Upload = {
|
||||
staticDir: string
|
||||
adminThumbnail?: string
|
||||
}
|
||||
|
||||
export type File = {
|
||||
data: Buffer
|
||||
mimetype: string
|
||||
name: string
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user