How to upload a file with NextJS and formidable? - next.js

I am trying to utilize formidable to access a file and eventually post it to a file storage system by following this post: Create upload files api in next.js. I am logging the data, but am getting confused about the output. When I try and access data.files.resume.filepath I get undefined.
The data looks like:
{ fields: {}, files: { resume: [ [PersistentFile] ] } }
And when I log data.files.resume I get this which shows the filepath:
[
PersistentFile {
_events: [Object: null prototype] { error: [Function (anonymous)] },
_eventsCount: 1,
_maxListeners: undefined,
lastModifiedDate: 2023-02-09T01:59:50.924Z,
filepath: 'C:\\Users\\Me\\AppData\\Local\\Temp\\21eef37a9191459ae49bb110f',
newFilename: '21eef37a9191459ae49bb110f',
originalFilename: 'thankyou.pdf',
mimetype: 'application/pdf',
hashAlgorithm: false,
size: 57285,
_writeStream: WriteStream {
fd: null,
path: 'C:\\Users\\Me\\AppData\\Local\\Temp\\21eef37a9191459ae49bb110f',
flags: 'w',
mode: 438,
start: undefined,
pos: undefined,
bytesWritten: 57285,
closed: false,
_writableState: [WritableState],
_events: [Object: null prototype],
_eventsCount: 1,
_maxListeners: undefined,
[Symbol(kFs)]: [Object],
[Symbol(kIsPerformingIO)]: false,
[Symbol(kCapture)]: false
},
hash: null,
[Symbol(kCapture)]: false
}
]
and when I log the data.files.resume.filepath I get undefined
Front End Form Submit:
const formData = new FormData();
formData.append("resume", resume);
const response = await fetch("/api/apply", {
method: "POST",
body: formData,
});
NextJS API:
import type { NextApiRequest, NextApiResponse } from "next";
import { IncomingForm } from "formidable";
export const config = {
api: {
bodyParser: false,
},
};
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
if (req.method !== "POST") {
return;
}
// parse form with a Promise wrapper
const data = await new Promise((resolve, reject) => {
const form = new IncomingForm();
form.parse(req, (err, fields, files) => {
if (err) return reject(err);
resolve({ fields, files });
});
});
console.log("------");
console.log(data);
console.log("------");
console.log(data.files.resume);
console.log("------");
console.log(data.files.resume.filepath); // this logs undefined
res.status(200).json(data);
}
I followed this StackOverflow article: Create upload files api in next.js and have tried logging each step, but still get undefined for the filepath. Any help or guidance is greatly appreciated!

Related

How to remain same current page pagination in redux rtk

I build an applicant with Redux RTK with createEntity
Two issue that I couldn't found it on the docs
CreateEntity is only return {ids: [], entities: []}? Is possible that return eg: totalPage from the response also?
Cache page only work on the hardcode initialState in createSlice if the pageQuery is same.
First question:
Getting the response from server was
{
users: [{id: 1}, ...]
totalPage: 100
}
I'd like to send totalPage to auto generated hook also.
export const usersAdapter = createEntityAdapter({})
export const initialState = usersAdapter.getInitialState()
export const usersApiSlice = apiSlice.injectEndpoints({
endpoints: (builder) => ({
getUsers: builder.query({
query: (args) => {
return {
url: '/api/users',
method: 'GET',
params: { page: 1, limit: 10 }
}
},
validateStatus: (response, result) => {
return response.status === 200 && !result.isError
},
transformResponse: (responseData) => {
const loadedUsers = responseData?.users.map((user) => user)
console.log("responseData: ", responseData) // <----- return { users: [], totalPage: 100 }. Could we set this totalPage value into Adapter?
return usersAdapter.setAll(initialState, loadedUsers)
},
providesTags: (result, error, arg) => {
if (result?.ids) {
return [
{ type: "User", id: "LIST" },
...result.ids.map((id) => ({ type: "User", id })),
]
} else return [{ type: "User", id: "LIST" }]
},
})
})
})
Use the hook in component
const { data } = useGetUsersQuery("page=1&limit=10");
console.log(data) // { ids: [], entity: [{}, {}] };
// expected return { ids: [], entity: [{}, {}], totalPage: 100}
Second question:
Store the page query in createSlice. The edit page will be remain same after refresh if the page query value same as initialState value.
import { createSlice } from "#reduxjs/toolkit"
const userReducer = createSlice({
name: "user",
initialState: {
query: `page=1&limit=10`,
},
reducers: {
setUserPageQuery: (state, action) => {
const query = action.payload
state.query = query
},
},
})
Page url Flow:
localhost:3000/users > localhost:3000/users/4 > refresh -> data will remain after refresh browser. (query "page=1&limit10" same as createSlice initialState value )
localhost:3000/users > localhost:3000/users/15 > refresh -> data state will gone after refresh browser. (query "page=2&limit10" different from createSlice initialState value )
Appreciate all the reply :)

Hapi basic auth validate is not called

The validate function for basic
await server.register(require('#hapi/basic'));
const validate = async (request, email, password, id_customer) => {
console.log(request)
if (!email || !password || !id_customer) {
return { credentials: null, isValid: false };
}
const results = await getHash(id_customer);
if (results.length == 0) {
return { credentials: null, isValid: false };
}
if (bcrypt.compareSync(password, results[0]['passwd'])) {
const credentials = { id: id_customer, email: email };
return { isValid: true, credentials };
}
return { credentials: null, isValid: false };
};
server.auth.strategy('simple', 'basic', { validate });
Route example :
{
method: 'POST',
path: '/home/getCategories',
config: {
auth: 'simple',
description: 'Get Home',
payload: {
multipart: true
},
handler: Home.getCategories
},
/* options: {
auth: 'simple'
},*/
//handler: Home.getCategories
},
Here is the axios call from the App :
axios.post('https://api.domain.com/home/getCategories', {
code: code
},
{
headers: {
'email': email,
'password': password,
'id_customer': id_customer
},
})
When I do the call I got a 401 unauthorized but I cant see the output of 'console.log(request)'
Any help ?
Have you tried the following? What version of Hapi.js are you using?
const categoryPostValidation = {
payload: Joi.object({
name: Joi.string().label("Name").min(1).max(30).error((errors) => new Error('Name is invalid, and must be 1 to 30 characters in length')).required(),
description: Joi.string().label("Description").min(1).max(255).error((errors) => new Error('Description is invalid, and must be 1 to 255 characters in length')).required()
}),
failAction: async (request, h, err) => {
throw err;
}
};
const categoryPostRouteOptions = {
description: "Posts one category.",
cors: true,
payload: {
output: 'data', // These are default options
parse: true // These are default options
},
auth: {
mode: 'required' // or 'try', etc
strategy: 'simple'
},
validate: categoryPostValidation,
handler: Home.getCategories
};
{
method: 'POST',
path: '/home/getCategories',
options: categoryPostRouteOptions
},

converting to redux tool kit and getting "Unhandled Rejection (TypeError): state.push is not a function"

i'm stuck while converting an old project to redux tool kit getting an "Unhandled Rejection (TypeError): state.push is not a function" error. i haven't got to grips with the action/thunk and reducer immutability yet. The alerts are working but then the error msg.
import axios from 'axios';
import { setAlert } from '../alerts/alertSlice';
const slice = createSlice({
name: 'auth',
initialState: {
token: localStorage.getItem('token'),
isAuthenticated: null,
loading: true,
user: null,
},
reducers: {
registerSuccess: (state, action) => {
const { payload } = action.payload;
state.push({
payload,
isAuthenticated: true,
loading: false,
});
},
registerFail: (state, action) => {
localStorage.removeItem('token');
state.push({
token: null,
isAuthenticated: false,
loading: false,
user: null,
});
},
},
});
const { registerSuccess, registerFail } = slice.actions;
export default slice.reducer;
// Register User
export const register =
({ name, email, password }) =>
async (dispatch) => {
const config = {
headers: {
'comtent-Type': 'application/json',
},
};
const body = JSON.stringify({ name, email, password });
try {
const res = await axios.post('/api/users', body, config);
dispatch({
type: registerSuccess,
payload: res.data,
});
} catch (err) {
const errors = err.response.data.errors;
if (errors) {
errors.forEach((error) => dispatch(setAlert(error.msg, 'danger')));
}
dispatch(registerFail());
}
};
.push is an array function to add a new item at the end of an array - your state is not an array.
You probably wanted to do something along the lines of
state.token = null
state.isAuthenticated = false
state.loading = false
state.user = null

NextJS routing error, when changing pages, the wrong file is trying to open

What I want
I want to change pages without next thinking I am trying to open another page.
The Problem
I have this weird routing problem.
First, my folder structure
pages
[app]
[object]
index.js
index.js
manager.js
feed.js
I am at this path /[app] and navigate to /[app]/manager and then I want to navigate to /[app]/feed and I get this Unhandled Runtime Error.
TypeError: Cannot read property "title" of undefined
This error comes from [object] index.js. Stacktrace is below. Of course, it makes sense it cannot read title because I am trying to open another page. And yet it thinks I am trying to open [object].
This error happens from time to time, but it doesn't matter in what order I try to open the pages, it can be manager to feed or feed to manager, or whatever else I have there.
My getStaticPaths and getStaticProps are the same on all these pages, I will share the one for manager.js.
export const getStaticPaths = async () => {
const paths = appRoutes.map((appRoute) => {
const slug = appRoute.slug;
return {
params: {
app: slug,
manager: 'manager',
},
};
});
return {
fallback: false,
paths,
};
};
export const getStaticProps = async ({ locale }) => {
return {
props: {
...(await serverSideTranslations(locale, ['manager', 'common'])),
},
};
};
And the same again, but for [object]:
export const getStaticPaths = async () => {
const allObjects = await loadObjectData({ id: 'all' });
const paths = allObjects.flatMap((object) => {
return appRoutes.map((appRoute) => {
return {
params: {
object: object.type,
app: appRoute.slug,
},
};
});
});
return {
fallback: false,
paths,
};
};
export const getStaticProps = async ({ params, locale }) => {
const object = await loadObjectData({ type: params.object });
const app = appRoutes.find((appRoute) => appRoute?.slug === params.app);
if (!object) {
throw new Error(
`${object} is not a valid Object. Try checking out your parameters: ${params.object}`
);
}
if (!app) {
throw new Error(`${app} is not a valid App.`);
}
return {
props: {
...(await serverSideTranslation(locale, ['common'])),
object,
app,
},
};
};
This error is hard to reproduce because it happens only from time to time.
New Edits
This is the full file of [object]/index.js
import appRoutes from '../../../routes/appRoutes';
import loadObjectData from '../../../utils/loadObjects';
import { serverSideTranslation } from 'next-i18next/serverSideTranslations';
export default function ObjectPage({ object }) {
return <h1> {object.title} </h1>;
}
export const getStaticPaths = async () => {
const allObjects = await loadObjectData({ id: 'all' });
const paths = allObjects.flatMap((object) => {
return appRoutes.map((appRoute) => {
return {
params: {
object: object.type,
app: appRoute.slug,
},
};
});
});
return {
fallback: false,
paths,
};
};
export const getStaticProps = async ({ params, locale }) => {
const object = await loadObjectData({ type: params.object });
const app = appRoutes.find((appRoute) => appRoute?.slug === params.app);
if (!object) {
throw new Error(
`${object} is not a valid Object. Try checking out your parameters: ${params.object}`
);
}
if (!app) {
throw new Error(`${app} is not a valid App.`);
}
return {
props: {
...(await serverSideTranslation(locale, ['common'])),
object,
app,
},
};
};
Stacktrace:
ObjectPage: index.js:6 Uncaught TypeError: Cannot read property 'title' of undefined
at ObjectPage (http://localhost:3000/_next/static/chunks/pages/%5Bapp%5D/%5Bobject%5D.js:3733:21)
at div
at Grid (http://localhost:3000/_next/static/chunks/pages/_app.js?ts=1624290251377:13654:35)
at WithStyles (http://localhost:3000/_next/static/chunks/pages/_app.js?ts=1624290251377:179881:31)
at div
at StyledComponent (http://localhost:3000/_next/static/chunks/pages/_app.js?ts=1624290251377:179652:28)
at div
at ProjectSelectionStore (http://localhost:3000/_next/static/chunks/pages/_app.js?ts=1624290251377:234820:77)
at Layout (http://localhost:3000/_next/static/chunks/pages/_app.js?ts=1624290251377:278:23)
at TaskStore (http://localhost:3000/_next/static/chunks/pages/_app.js?ts=1624290251377:235454:77)
at UserDocumentStore (http://localhost:3000/_next/static/chunks/pages/_app.js?ts=1624290251377:235663:77)
at StoneStore (http://localhost:3000/_next/static/chunks/pages/_app.js?ts=1624290251377:235119:77)
at StoreMall (http://localhost:3000/_next/static/chunks/pages/_app.js?ts=1624290251377:409:23)
at ThemeProvider (http://localhost:3000/_next/static/chunks/pages/_app.js?ts=1624290251377:178584:24)
at App (http://localhost:3000/_next/static/chunks/pages/_app.js?ts=1624290251377:234333:24)
at I18nextProvider (http://localhost:3000/_next/static/chunks/pages/_app.js?ts=1624290251377:224427:19)
at AppWithTranslation
at ErrorBoundary (http://localhost:3000/_next/static/chunks/main.js?ts=1624290251377:146:47)
at ReactDevOverlay (http://localhost:3000/_next/static/chunks/main.js?ts=1624290251377:250:23)
at Container (http://localhost:3000/_next/static/chunks/main.js?ts=1624290251377:8662:5)
at AppContainer (http://localhost:3000/_next/static/chunks/main.js?ts=1624290251377:9151:24)
at Root (http://localhost:3000/_next/static/chunks/main.js?ts=1624290251377:9282:24)
25.06.2021
So I consoled logged the router from the ObjectPage and for each NavigationItem. I noticed something strange.
This is the href I am passing to teh <Link>:
{
pathname: "/[app]/[menuItem]"
query: {
app: "content"
menuItem: "files"
}
}
And this is the full router I am getting back on ObjectPage.
{
asPath: "/content/editor" // this the path i want to open
back: ƒ ()
basePath: ""
beforePopState: ƒ ()
components: {
"/[app]/[object]": {styleSheets: Array(0), __N_SSG: true, __N_SSP: undefined, props: {…}, Component: ƒ}
"/[app]/editor": {initial: true, props: {…}, err: undefined, __N_SSG: true, Component: ƒ, …}
"/_app": {styleSheets: Array(0), Component: ƒ}
}
defaultLocale: "de"
events: {on: ƒ, off: ƒ, emit: ƒ}
isFallback: false
isLocaleDomain: false
isPreview: false
isReady: true
locale: "de"
locales: ["de"]
pathname: "/[app]/[object]" // [object] is being loaded
prefetch: ƒ ()
push: ƒ ()
query: {app: "content", menuItem: "editor", object: "editor"} // this is interesting
reload: ƒ ()
replace: ƒ ()
route: "/[app]/[object]" // same as pathname
}
In the query you can see object was injected. But I cannot tell from where and why.
I had this code:
{
pathname: "/[app]/[menuItem]"
query: {
app: "content"
menuItem: "files"
}
}
This was incorrect because there is no dynamic path to [menuItem]. So instead I wrote:
{
pathname: "/[app]/files"
query: {
app: "content"
}
}
Which fixed the issue I had.
I have misunderstood the docs for parameters.

How to upload a batch of images to Azure Custom Vision using JavaScript

I want to upload a batch of 64 images to Custom Vision using the JavaScript SDK.
const files: ImageFileCreateEntry[] = []
while (i < 64) {
const data = fs.readFileSync(`${sampleDataRoot}/${tag.name}/${file}`)
const fileEntry: ImageFileCreateEntry = { name: file, contents: data }
files.push(fileEntry);
i++
}
const batch: ImageFileCreateBatch = { images: files, tagIds: [tag.id] }
fileUploadPromises.push(client.createImagesFromFiles(projectId, batch))
But I'm getting the following error:
RestError: No valid image files
at new RestError (/home/pomatti/projects/personal/azure-customvision-benchmark/node_modules/#azure/ms-rest-js/lib/restError.ts:18:5)
at /home/pomatti/projects/personal/azure-customvision-benchmark/node_modules/#azure/ms-rest-js/lib/policies/deserializationPolicy.ts:117:27
at processTicksAndRejections (internal/process/task_queues.js:97:5)
at async Promise.all (index 5) {
code: 'BadRequestImageBatch',
statusCode: 400,
request: WebResource {
streamResponseBody: false,
url: 'https://eastus.api.cognitive.microsoft.com/customvision/v3.3/training/projects/ff4967f9-f772-4473-89a5-41356c471454/images/files',
method: 'POST',
headers: HttpHeaders { _headersMap: [Object] },
body: '{"images":[],"tagIds":["8a2e95b5-2050-403d-95e7-45b08e358d7d"]}',
query: undefined,
formData: undefined,
withCredentials: false,
abortSignal: undefined,
timeout: 0,
onUploadProgress: undefined,
onDownloadProgress: undefined,
proxySettings: undefined,
keepAlive: undefined,
operationSpec: {
httpMethod: 'POST',
path: 'projects/{projectId}/images/files',
urlParameters: [Array],
requestBody: [Object],
responses: [Object],
serializer: [Serializer]
}
},
response: {
body: '{"code":"BadRequestImageBatch","message":"No valid image files"}',
headers: HttpHeaders { _headersMap: [Object] },
status: 400
},
body: { code: 'BadRequestImageBatch', message: 'No valid image files' }
}
I figured out that my implementation was completely messed up.
This is how I solved it for now:
const client = getTrainingClient();
const tags = await client.getTags(projectId);
let fileUploadPromises = [];
tags.forEach(tag => {
const imageFiles = fs.readdirSync(`${sampleDataRoot}/${tag.name}`);
const files: ImageFileCreateEntry[] = []
const chunk = imageFiles.slice(0, 64)
chunk.forEach(file => {
const data = fs.readFileSync(`${sampleDataRoot}/${tag.name}/${file}`)
const fileEntry: ImageFileCreateEntry = { name: file, contents: data }
files.push(fileEntry);
})
const batch: ImageFileCreateBatch = { images: files, tagIds: [tag.id] }
fileUploadPromises.push(client.createImagesFromFiles(projectId, batch))
})

Resources