diskStorage() not found in nestJs multer file upload - nestjs

i want use multer in my nestJs application like this:
#Post()
#UseInterceptors(
FileInterceptor('file', {
storage: diskStorage({
destination: './files',
}),
}),
)
async upload(#Request() req, #Query() query: any, #UploadedFile() file) {
console.log(file);
}
But my IDE (vscode) keeps saying: Cannot find name 'diskStorage' and is not compiling.
I also register the MulterModule on the specified Module (FeatureModule).
I can use
#UseInterceptors(
FileInterceptor('file', { dest: '/data-path'}),
)
But i want change the filename for example. For this I need the diskStorage function
What can i do, to resolve the issue?

the solution was as described by #JayMcDoniel
An extra import had to be made.
import { diskStorage } from 'multer';
With the help of this example, I was able to recreate and solve the whole thing again.
https://github.com/TannerGabriel/Blog/tree/091cbf99bc9409629e1ab717ca8ec405c421d6d4/nest-file-uploading

Related

Firebase Storage: what is `byteLength`? How to set MIME types of file uploads?

When I upload a .mp3 audio file or a .jpg picture to Firebase Cloud Storage I get this error:
TypeError: Cannot read properties of undefined (reading 'byteLength')
What is byteLength?
I tried uploading a .jpg with raw uploadString. The file appeared in Storage but it was only 15B when it should have been 100KB.
The documentation Upload files with Cloud Storage on Web doesn't say anything about specifying MIME types when uploading files.
Writing to the Storage emulator executes without errors but no files appear.
Here's the Firebase Cloud Function I'm using.
import { initializeApp } from "firebase/app";
import * as functions from "firebase-functions";
import { getStorage, ref, uploadBytes, uploadString, connectStorageEmulator } from "firebase/storage";
const firebaseConfig = {
apiKey: "...",
authDomain: "my-awesome-app.firebaseapp.com",
databaseURL: "https://my-awesome-app.firebaseio.com",
projectId: "my-awesome-app",
storageBucket: "my-awesome-app.appspot.com",
messagingSenderId: "...",
appId: "..."
};
const app = initializeApp(firebaseConfig);
export const ByteMe = functions.firestore.document('ByteMe/{userID}').onUpdate((change, context) => {
const storage = getStorage();
const storageRef = ref(storage, 'gs://my-awesome-app.appspot.com/Pictures/bootchkas.jpg');
connectStorageEmulator(storage, "localhost", 9199); // comment out to write to the cloud
async function uploadPicture() {
try {
uploadString(storageRef, './bootchkas.jpg').then((snapshot) => {
console.log('Uploaded a raw string!');
});
} catch (error) {
console.error(error);
}
}
return uploadPicture();
});
As #Dharmaraj indicated, it looks like you are just uploading the string ./bootchkas.jpg -> its exactly 15 bytes long. Is this the code you wanted to use to upload the file? If it is, it won't find the file, but only find the string ./bookchkas.jpg.
If you want to upload a file as a string you would either need to encode the file as base64 or load the file itself and send that to firebase storage. If using the browser APIs, you can fetch the file using something akin to this. If using nodejs and a server side application, you will want to refer to the file using the filesystem nodejs package.
As #Dharmaraj and #Alexander N. said, uploadString just uploads the subsequent string, not a file. The error message
TypeError: Cannot read properties of undefined (reading '...')
means that undefined is a missing ES module that you didn't import.
The following code almost uploads a file...with one problem.
import file from "./bootchkas.jpg";
export const ByteMe = functions.firestore.document('ByteMe/{userID}').onUpdate((change, context) => {
const storage = getStorage();
const storageRef = ref(storage, 'gs://my-awesome-app.appspot.com/Pictures/bootchkas.jpg'); // location to write to
connectStorageEmulator(storage, "localhost", 9199); // comment out to write to the cloud
const metadata = {
contentType: 'image/jpeg',
};
async function uploadPicture() {
try {
await uploadBytes(storageRef, file, metadata);
console.log('Uploaded a file!');
} catch (error) {
console.error(error);
}
}
return uploadPicture();
});
uploadBytes uploads files (or blobs). It take three parameters: the storageRef or path to a location in Storage; file, which is the file to be uploaded to Storage; and metadata, which includes the MIME type of the file.
I've set the MIME contentType to 'image/jpeg'.
This leaves file. We need to import the file as an ES module:
import file = './bootchkas.jpg`;
But that doesn't work because bootchkas.jpg isn't an ES module.
We need to use WebPack to make an ES module from bootchkas.jpg. The WebPack documentation has a section Loading Images. I followed the tutorial and was able to load bootchkas.com into a HTML page. But when I tried to use WebPack with my Firebase Cloud Function it threw error after error.
Here's the webpack.config.js I ended up with (bizarrely, the tutorial use CommonJS modules):
import path from "path"
const __dirname = "/Users/TDK/VisualStudioCodeProjects/CloudFunctions/functions"
const exportMe = {
mode: 'development',
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist'),
},
module: {
rules: [
{
test: /\.(png|svg|jpg|jpeg|gif)$/i,
type: 'asset/resource',
},
],
},
};
export default exportMe;
This throws about a half-dozen error, which I fixed one by one.
Then I restarted the Firebase emulator and it couldn't read functions/package.json. WebPack requires changing "main": "./src/index.js", to "private": true, which makes Firebase unable to reach your Cloud Functions. Trying to get WebPack to work with Firebase Cloud Functions is too difficult.

how can cover FileInterceptor lines of code in unit test case nestjs

I have mention below method in my controller.What parameter I should add in my test file and pass to cover it?
#UseInterceptors(
FileInterceptor('file', {
storage: diskStorage({
destination: './uploads',
filename: (req, file, cb) => {
if (file) {
const randomName = Array(32)
.fill(null)
.map(() => Math.round(Math.random() * 16).toString(16))
.join('');
cb(null, `${randomName}${extname(file.originalname)}`);
}
}
})
})
)
How i can cover these lines of code in test nestjs using jest. In whole controller only these line of code missing in coverage area.
Either pull the callback to a separate location so you can test it directly, or test it using an e2e test and actually sending the request into the server.

Can't access buffer in FileInterceptor while uploading file with nestJS/multer

I'm trying to access the buffer of a file uploaded with Postman to a simple controller in Nest. The buffer exists in the request object, but I can't access it in the fileFilter of the FileInterceptor.
Example:
#Post('file')
#UseInterceptors(FileInterceptor('file'))
async testUpload(#UploadedFile() file) {
// defined!
console.log(file.buffer)
}
But when I try and access the file from the interceptor the buffer isn't present
#UseInterceptors(FileInterceptor('file', {
fileFilter: (req, file, cb) => {
// undefined
console.log(file.buffer)
cb(null, true)
}
}))
async testUpload(#UploadedFile() file) {
// defined!
console.log(file.buffer)
}
Am I missing something here?

Cannot upload a file in nestJS when the network conditions are poor

I have implemented in a controller a route to upload files from axios. For that I use FileInterceptor. Everything works properly but as soon as I activate network throttling in the browser the uploader does not work. Here is the snippet:
#Post('/upload')
#UseInterceptors(
FileInterceptor("file", {
storage: diskStorage({
destination: './src/uploads',
// editFileName is a function that changes the name of the file
filename: editFileName,
}),
})
)
uploadFile(#UploadedFile() file) {
try {
console.log(file)
return 'ok';
} catch(e) {
console.log(e)
}
}
This is the following error that I get:
POST http://localhost:3003/user/file/upload net::ERR_CONNECTION_RESET
It seems, it tries to connect twice to complete the process. First connection time is 60s and the second one also but after that it throws the above error.
I try many ways changing the timeout param but in none of them not success.
The first one is changing the destination config param of diskStorage from string to function:
destination: (req, file, callback) => {
console.log(file);
// That output I see when it uploads successfully in a normal network condition (wifi)
// but it ignores the timer time. Usually is after 5 seconds.
req.socket.setTimeout(30000, () => console.log('Socket ErrorTimeout'))
req.setTimeout(5000, () => console.log('Error req timeout'))
callback(null, './src/uploads')
}
Next one is, inside of bootstrap function on the main file adding the following snippet:
const server = await app.listen(3003);
server.on('connection', (socket) => {
console.log('Connected!')
socket.setTimeout(5*10*1000)
})
And finally adding another module in FileUploader module but also not success:
#Module({
imports: [
MulterModule.register({
dest: './src/uploads'
}),
HttpModule.register({
timeout: 90000,
maxRedirects: 5,
})
],
controllers: [FilesController]
})
export class FilesModule {}
I recommend to use minio, its so good to store files and very good documentation

Limit express-busboy to specific routes

I set up file uploads with express-busboy using the example from the repository here
which doesn't seem to use the normal use() syntax so I'm a little confused as to how to actually limit this middleware so it only executes on a specific route because it's breaking other POST requests.
This is how I configured it:
var busboy = require('express-busboy');
busboy.extend(app, {
upload: true,
path: './uploads/temp'
});
In allowedPath value you can specify regex in this case limit at post route defined in express application. likes /uploads
busboy.extend(app, {
upload: true,
path: './uploads/temp',
allowedPath: /^\/uploads$/
});
or other wise you can pass function
var options = {
upload: true,
path: './uploads/temp',
};
options.allowedPath = function(url) {
return url == '/api/ccUpload';
}
busboy.extend(app, options);
Well since express-busboy wasn't working for me, I tried using express-fileupload instead and that seems to work now.
Try using Multer instead, and limit it to your route:
app.post('/^\/api\/ccUpload$/',
multer({
dest: './uploads/temp',
rename: function(fieldname, filename, req, res) {
return filename.toLowerCase();
}
}),
yourRouteHandler
);

Resources