// From storage.ts:13-20const getMount = (): string | undefined => { try { return useRuntimeConfig().public.fileStorage.mount } catch { // when running outside of a Nuxt context (tests), fall back to env var return process.env.FILE_STORAGE_MOUNT || process.env.NUXT_FILE_STORAGE_MOUNT }}
All file paths are resolved relative to this mount point, and the security layer ensures files cannot escape this boundary.
// From storage.ts:59-80let filename: stringif (typeof fileNameOrIdLength === 'number') { filename = `${generateRandomId(fileNameOrIdLength)}.${safeExt}`} else { ensureSafeBasename(fileNameOrIdLength) const extensionFromFileName = fileNameOrIdLength.split('.').pop() if (!fileNameOrIdLength.includes('.')) { // Case 1: No extension → append the correct one filename = `${fileNameOrIdLength}.${safeExt}` } else if (extensionFromFileName === safeExt) { // Case 2: Correct extension → use as-is filename = fileNameOrIdLength } else { // Case 3: Wrong extension → warn and replace it console.warn( `[nuxt-file-storage] The provided filename "${fileNameOrIdLength}" does not have the expected extension ".${safeExt}". The correct extension will be appended.` ) filename = `${fileNameOrIdLength.split('.').slice(0, -1).join('.')}.${safeExt}` }}
Extension Cases:
// Case 1: No extension providedawait storeFileLocally(jpegFile, 'document', '/files')// Returns: "document.jpeg"// Case 2: Correct extension providedawait storeFileLocally(jpegFile, 'document.jpeg', '/files')// Returns: "document.jpeg"// Case 3: Wrong extension provided (will warn)await storeFileLocally(jpegFile, 'document.png', '/files')// Console: Warning about wrong extension// Returns: "document.jpeg"
Custom filenames are validated for security. They must not contain path separators, null bytes, or be . or ..
// From path-safety.ts:5-9export const normalizeRelative = (p: string): string => { if (!p) return '' // Remove leading slashes/backslashes and normalize separators return p.replace(/^[/\\]+/, '').replace(/\\/g, '/')}
All paths are normalized before use:
// These all resolve to the same location:await storeFileLocally(file, 8, '/uploads')await storeFileLocally(file, 8, 'uploads')await storeFileLocally(file, 8, '\\uploads')// All save to: {mount}/uploads/{filename}
// From storage.ts:85-104const dirPath = await resolveAndEnsureInside(location, normalizedFilelocation)try { await mkdir(dirPath, { recursive: true })} catch (err: any) { if (err?.code === 'EEXIST') { throw new Error( `[nuxt-file-storage] EEXIST: A file already exists at "${dirPath}" where a directory was expected.` ) } else if (err?.code === 'ENOTDIR') { throw new Error( `[nuxt-file-storage] ENOTDIR: Cannot create directory "${dirPath}" because a parent path component is a file, not a directory.` ) } throw err}
Directories are created automatically with detailed error handling:
// No need to create directories manuallyawait storeFileLocally(file, 8, '/deeply/nested/path')// Creates: {mount}/deeply/nested/path/ automatically
// From storage.ts:48-58const { binaryString, ext } = parseDataUrl(file.content)// Extract the file extension from the original filenameconst nameStr = file.name.toString()// If the provided filename contains a dot, use that extension; // otherwise fall back to MIME-derived extconst originalExt = nameStr.includes('.') ? nameStr.split('.').pop() : ext// sanitize extension (keep alphanumerics only)const safeExt = (originalExt || ext).replace(/[^a-zA-Z0-9]/g, '') || ext
Extension sanitization prevents path traversal and injection attacks, but you should still validate allowed file types based on your application’s needs.