mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-17 17:00:15 +01:00
🖼️ fix: Clipboard Files & File Name Issues (#2015)
* fix: ensure image handling fetchs image to base64 for multiple images * fix: append file_id's when writing uploaded files * feat: timestamp files uploaded from clipboard * chore: add a different fileid+name separator
This commit is contained in:
parent
18edd2660b
commit
40e884b3ec
5 changed files with 31 additions and 18 deletions
|
|
@ -26,7 +26,7 @@ const { logger } = require('~/config');
|
||||||
* - width: The width of the converted image.
|
* - width: The width of the converted image.
|
||||||
* - height: The height of the converted image.
|
* - height: The height of the converted image.
|
||||||
*/
|
*/
|
||||||
async function uploadImageToFirebase({ req, file, endpoint, resolution = 'high' }) {
|
async function uploadImageToFirebase({ req, file, file_id, endpoint, resolution = 'high' }) {
|
||||||
const inputFilePath = file.path;
|
const inputFilePath = file.path;
|
||||||
const inputBuffer = await fs.promises.readFile(inputFilePath);
|
const inputBuffer = await fs.promises.readFile(inputFilePath);
|
||||||
const {
|
const {
|
||||||
|
|
@ -38,7 +38,7 @@ async function uploadImageToFirebase({ req, file, endpoint, resolution = 'high'
|
||||||
const userId = req.user.id;
|
const userId = req.user.id;
|
||||||
|
|
||||||
let webPBuffer;
|
let webPBuffer;
|
||||||
let fileName = path.basename(inputFilePath);
|
let fileName = `${file_id}__${path.basename(inputFilePath)}`;
|
||||||
if (extension.toLowerCase() === '.webp') {
|
if (extension.toLowerCase() === '.webp') {
|
||||||
webPBuffer = resizedBuffer;
|
webPBuffer = resizedBuffer;
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ const { updateFile } = require('~/models/File');
|
||||||
* representing the user, and an `app.locals.paths` object with an `imageOutput` path.
|
* representing the user, and an `app.locals.paths` object with an `imageOutput` path.
|
||||||
* @param {Express.Multer.File} params.file - The file object, which is part of the request. The file object should
|
* @param {Express.Multer.File} params.file - The file object, which is part of the request. The file object should
|
||||||
* have a `path` property that points to the location of the uploaded file.
|
* have a `path` property that points to the location of the uploaded file.
|
||||||
|
* @param {string} params.file_id - The file ID.
|
||||||
* @param {EModelEndpoint} params.endpoint - The params object.
|
* @param {EModelEndpoint} params.endpoint - The params object.
|
||||||
* @param {string} [params.resolution='high'] - Optional. The desired resolution for the image resizing. Default is 'high'.
|
* @param {string} [params.resolution='high'] - Optional. The desired resolution for the image resizing. Default is 'high'.
|
||||||
*
|
*
|
||||||
|
|
@ -28,7 +29,7 @@ const { updateFile } = require('~/models/File');
|
||||||
* - width: The width of the converted image.
|
* - width: The width of the converted image.
|
||||||
* - height: The height of the converted image.
|
* - height: The height of the converted image.
|
||||||
*/
|
*/
|
||||||
async function uploadLocalImage({ req, file, endpoint, resolution = 'high' }) {
|
async function uploadLocalImage({ req, file, file_id, endpoint, resolution = 'high' }) {
|
||||||
const inputFilePath = file.path;
|
const inputFilePath = file.path;
|
||||||
const inputBuffer = await fs.promises.readFile(inputFilePath);
|
const inputBuffer = await fs.promises.readFile(inputFilePath);
|
||||||
const {
|
const {
|
||||||
|
|
@ -45,7 +46,8 @@ async function uploadLocalImage({ req, file, endpoint, resolution = 'high' }) {
|
||||||
fs.mkdirSync(userPath, { recursive: true });
|
fs.mkdirSync(userPath, { recursive: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
const newPath = path.join(userPath, path.basename(inputFilePath));
|
const fileName = `${file_id}__${path.basename(inputFilePath)}`;
|
||||||
|
const newPath = path.join(userPath, fileName);
|
||||||
|
|
||||||
if (extension.toLowerCase() === '.webp') {
|
if (extension.toLowerCase() === '.webp') {
|
||||||
const bytes = Buffer.byteLength(resizedBuffer);
|
const bytes = Buffer.byteLength(resizedBuffer);
|
||||||
|
|
|
||||||
|
|
@ -39,25 +39,24 @@ async function encodeAndFormat(req, files, endpoint) {
|
||||||
for (let file of files) {
|
for (let file of files) {
|
||||||
const source = file.source ?? FileSources.local;
|
const source = file.source ?? FileSources.local;
|
||||||
|
|
||||||
if (encodingMethods[source]) {
|
if (!encodingMethods[source]) {
|
||||||
promises.push(encodingMethods[source](req, file));
|
const { prepareImagePayload } = getStrategyFunctions(source);
|
||||||
continue;
|
if (!prepareImagePayload) {
|
||||||
|
throw new Error(`Encoding function not implemented for ${source}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
encodingMethods[source] = prepareImagePayload;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { prepareImagePayload } = getStrategyFunctions(source);
|
const preparePayload = encodingMethods[source];
|
||||||
if (!prepareImagePayload) {
|
|
||||||
throw new Error(`Encoding function not implemented for ${source}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
encodingMethods[source] = prepareImagePayload;
|
/* Google & Anthropic don't support passing URLs to payload */
|
||||||
|
|
||||||
/* Google doesn't support passing URLs to payload */
|
|
||||||
if (source !== FileSources.local && base64Only.has(endpoint)) {
|
if (source !== FileSources.local && base64Only.has(endpoint)) {
|
||||||
const [_file, imageURL] = await prepareImagePayload(req, file);
|
const [_file, imageURL] = await preparePayload(req, file);
|
||||||
promises.push([_file, await fetchImageToBase64(imageURL)]);
|
promises.push([_file, await fetchImageToBase64(imageURL)]);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
promises.push(prepareImagePayload(req, file));
|
promises.push(preparePayload(req, file));
|
||||||
}
|
}
|
||||||
|
|
||||||
const detail = req.body.imageDetail ?? 'auto';
|
const detail = req.body.imageDetail ?? 'auto';
|
||||||
|
|
|
||||||
|
|
@ -185,7 +185,12 @@ const processImageFile = async ({ req, res, file, metadata }) => {
|
||||||
const source = req.app.locals.fileStrategy;
|
const source = req.app.locals.fileStrategy;
|
||||||
const { handleImageUpload } = getStrategyFunctions(source);
|
const { handleImageUpload } = getStrategyFunctions(source);
|
||||||
const { file_id, temp_file_id, endpoint } = metadata;
|
const { file_id, temp_file_id, endpoint } = metadata;
|
||||||
const { filepath, bytes, width, height } = await handleImageUpload({ req, file, endpoint });
|
const { filepath, bytes, width, height } = await handleImageUpload({
|
||||||
|
req,
|
||||||
|
file,
|
||||||
|
file_id,
|
||||||
|
endpoint,
|
||||||
|
});
|
||||||
const result = await createFile(
|
const result = await createFile(
|
||||||
{
|
{
|
||||||
user: req.user.id,
|
user: req.user.id,
|
||||||
|
|
|
||||||
|
|
@ -179,7 +179,14 @@ export default function useTextarea({
|
||||||
if (e.clipboardData && e.clipboardData.files.length > 0) {
|
if (e.clipboardData && e.clipboardData.files.length > 0) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
setFilesLoading(true);
|
setFilesLoading(true);
|
||||||
handleFiles(e.clipboardData.files);
|
const timestampedFiles: File[] = [];
|
||||||
|
for (const file of e.clipboardData.files) {
|
||||||
|
const newFile = new File([file], `clipboard_${+new Date()}_${file.name}`, {
|
||||||
|
type: file.type,
|
||||||
|
});
|
||||||
|
timestampedFiles.push(newFile);
|
||||||
|
}
|
||||||
|
handleFiles(timestampedFiles);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[handleFiles, setFilesLoading, setText],
|
[handleFiles, setFilesLoading, setText],
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue