Es ist derzeit nicht möglich, Dateien von API-Routen oder -Seiten zu lesen.
Ich möchte fs.readFile
mit einem __dirname
Pfad aufrufen und es "einfach funktionieren" lassen.
Dies sollte im Entwicklungs- und Produktionsmodus funktionieren.
Dies muss möglicherweise in gewisser Weise in @zeit/webpack-asset-relocator-loader
werden. Dieses Plugin verarbeitet diese Arten von Anforderungen.
Es ist jedoch keine Notwendigkeit. Ich wäre in Ordnung mit etwas, das _nur_ mit __dirname
und __filename
funktioniert (keine relativen oder cwd-basierten Pfade).
Beispiel:
// pages/api/test.js
import fs from 'fs'
import path from 'path'
export default (req, res) => {
const fileContent = fs.readFileSync(
path.join(__dirname, '..', '..', 'package.json'),
'utf8'
)
// ...
}
Hinweis: Ich weiß, dass Sie das obige Beispiel ☝️ mit
require
betrügen können, aber das ist nicht der Punkt. 😄
Ich wollte das nur unterstreichen und versuchen, das Hochladen von Dateien mithilfe von API-Routen zu implementieren. Ich kann die Datei hochladen, muss dann aber wieder darauf zugreifen können, um sie in den S3-Bucket hochzuladen.
Ich unterstütze das! Außerdem ist es für die Nutzung meines Unternehmens sehr wichtig, Verzeichnisse lesen zu können, da wir unsere Daten wie Teammitglieder und Blog-Posts in einem Inhaltsverzeichnis speichern und nach einer Möglichkeit suchen, alle Dateien in dem Verzeichnis zu benötigen.
Der obige PR wird dies beheben! ☝️ 🙏
Wie wäre es mit fs.writeFile
ist das möglich? Erstellen und speichern Sie beispielsweise eine JSON-Datei basierend auf einem Webhook, der auf einem /api/route
gepostet wurde
Hey @marlonmarcello , das wird möglich sein. Bleib dran
Ist das schon gelöst?
Noch nicht, Sie können #8334 abonnieren
@huv1k Vielen Dank!
Gibt es eine Möglichkeit, diesen Schritt schneller voranzutreiben?
Hinweis: Wenn Sie TypeScript verwenden, können Sie bereits eine JSON-Datei direkt als Modul importieren (stellen Sie sicher, dass resolveJsonModule
true
in tsconfig.json
). Z.B:
import myJson from '../../../some/path/my.json';
Die Form des JSON-Objekts wird auch automatisch als Typ verwendet, also ist die automatische Vervollständigung wirklich nett.
Problemumgehung, die ich verwende:
# next.config.js
module.exports = {
serverRuntimeConfig: {
PROJECT_ROOT: __dirname
}
}
und an der stelle brauchst du den weg
import fs from 'fs'
import path from 'path'
import getConfig from 'next/config'
const { serverRuntimeConfig } = getConfig()
fs.readFile(path.join(serverRuntimeConfig.PROJECT_ROOT, './path/to/file.json'))
Ich weiß, dass dies nicht die Notwendigkeit löst, auf Dateien mit Pfaden relativ zur aktuellen Datei zu verweisen, aber dies löst meinen sehr verwandten Anwendungsfall (Lesen von Bilddateien aus einem /public/images
Ordner).
Habe in der PR gesehen, dass sich dies ein wenig geändert hat - gibt es ein Update zu den aktuellen Plänen (oder nicht)? Klingt so, als gäbe es einige Strategien, die Sie nicht verfolgen möchten. Denken Sie daran, sie aufzulisten + warum können Mitwirkende dies versuchen?
Dies blockiert die Verwendung von Nexus mit Next.js. Es wäre schön, wenn dies noch einmal priorisiert würde.
Problemumgehung, die ich verwende:
# next.config.js module.exports = { serverRuntimeConfig: { PROJECT_ROOT: __dirname } }
und an der stelle brauchst du den weg
import fs from 'fs' import path from 'path' import getConfig from 'next/config' const { serverRuntimeConfig } = getConfig() fs.readFile(path.join(serverRuntimeConfig.PROJECT_ROOT, './path/to/file.json'))
Ich weiß, dass dies nicht die Notwendigkeit löst, auf Dateien mit Pfaden relativ zur aktuellen Datei zu verweisen, aber dies löst meinen sehr verwandten Anwendungsfall (Lesen von Bilddateien aus einem
/public/images
Ordner).
Toller Kerl. Hat bei mir funktioniert.
Ich habe dafür die neue Methode getStaticProps
verwendet (in #9524). Die Methode ist derzeit als instabil markiert, aber das Next.js-Team scheint gute Unterstützung bei der offiziellen Auslieferung zu geben.
z.B:
export async function unstable_getStaticProps() {
const siteData = await import("../data/pages/siteData.json");
const home = await import("../data/pages/home.json");
return {
props: { siteData, home }
};
}
@ ScottSmith95 Haben Sie ein öffentliches Quellprojekt, in dem Sie dies verwenden? Neugierig, wie es aussehen würde.
Das Projekt ist noch nicht Open Source, aber ich freue mich, mehr von meiner Konfiguration zu teilen, wenn Sie weitere Fragen haben.
@ScottSmith95 Ich habe _alle_ die Fragen 😛
src
?)@Svish Wir speichern Datendateien in /data innerhalb unseres Projekts. (Seiten befinden sich in /pages, nicht in /src/prages.) Diese Seitenkomponente sieht so aus (Requisiten werden an die Home-Komponente gesendet, die der Standardexport ist):
// /pages/index.js
const Home = ({ siteData, home }) => {
return (
<>
<Head>
<meta name="description" content={siteData.siteDescription} />
<meta name="og:description" content={siteData.siteDescription} />
<meta
name="og:image"
content={getAbsoluteUrl(siteData.siteImage, constants.siteMeta.url)}
/>
</Head>
<section className={`container--fluid ${styles.hero}`}>
<SectionHeader section={home.hero} heading="1">
<div className="col-xs-12">
<PrimaryLink
href={home.hero.action.path}
className={styles.heroAction}
>
{home.hero.action.text}
</PrimaryLink>
</div>
</SectionHeader>
<div className={styles.imageGradientOverlay}>
<img src={home.hero.image.src} alt={home.hero.image.alt} />
</div>
</section>
</>
);
};
Bei fortgeschritteneren Seiten mit dynamischen Routen erfassen wir diese Daten wie folgt:
// /pages/studio/[member.js]
export async function unstable_getStaticProps({ params }) {
const siteData = await import("../../data/pages/siteData.json");
const member = await import(`../../data/team/${params.member}.json`);
return {
props: { siteData, member }
};
}
Die Bereitstellung verläuft wirklich reibungslos, mit dynamischen Routen werden getStaticPaths()
benötigt. Ich empfehle Ihnen, die Dokumentation dazu im RFC zu lesen, aber hier ist ein Beispiel dafür, wie wir damit umgehen, indem wir alle Daten unserer Teammitglieder sammeln und an Next.js übergeben.
// /pages/studio/[member.js]
export async function unstable_getStaticPaths() {
const getSingleFileJson = async path => await import(`../../${path}`);
// These utility functions come from `@asmallstudio/tinyutil` https://github.com/asmallstudio/tinyutil
const directoryData = await getDirectory(
"./data/team",
".json",
getSingleFileJson,
createSlugFromTitle
);
const directoryPaths = directoryData.reduce((pathsAccumulator, page) => {
pathsAccumulator.push({
params: {
member: page.slug
}
});
return pathsAccumulator;
}, []);
return directoryPaths;
}
@ ScottSmith95 Sieht vielversprechend aus! Ein paar Anschlussfragen, wenn Sie Zeit haben:
next export
?getStaticPaths
eine Liste von Pfadparametern zurückgibt, die dann (von next) nacheinander für jedes Rendern in getStaticProps
eingespeist wird?getStaticProps
ohne getStaticPaths
, zum Beispiel für eine Seite ohne Parameter?getStaticProps
in _app
? Zum Beispiel, wenn Sie eine Site-weite Konfiguration haben, die Sie laden möchten, oder so ähnlich?Was ist mit der Apis?? Diese Hooks sind für Seiten, aber was ist mit Apis?
Ich bin verwirrt. Ich konnte den _dirname als env-Variable in der nächsten config setzen. Daher konnte ich von der API auf das Dateisystem zugreifen, aber es funktionierte nur lokal. Nachdem ich es bis jetzt bereitgestellt habe, habe ich einen Fehler erhalten. Irgendwelche Ideen, warum es nach der Bereitstellung nicht funktioniert?
@josias-r Das Hauptproblem besteht normalerweise darin, dass die zu lesenden Dateien nicht in der Bereitstellung enthalten sind, aber es hängt davon ab, wie Sie sie einschließen und um welche Dateitypen es sich handelt ( js
/ json
ist normalerweise in Ordnung, aber andere Dateitypen wie .jade
erfordern alternative Wege, um mit ihm umzugehen, wie die Verwendung eines separaten @now/node
Lambda/Deployment zum Lesen/Verarbeiten dieser Dateien).
Wenn Sie den Fehler genauer erklären können, kann Ihnen vielleicht jemand helfen.
@BrunoBernardino Es bezog sich tatsächlich auf JSON-Dateien in meinem src-Ordner. Aber es ist sogar die Methode fs.readdirSync(my_dirname_env_var)
, die bereits bei der Bereitstellung fehlschlägt. Dieses Verzeichnis scheint nach der Bereitstellung also überhaupt nicht zu existieren. Folgendes erhalte ich, wenn ich versuche, auf den vollständigen Pfad zum Json über meine API zuzugreifen:
ERROR Error: ENOENT: no such file or directory, open '/zeit/3fc37db3/src/content/somejsonfilethatexists.json'
Und wie ich bereits erwähnt habe, funktioniert dies lokal, wenn ich npm start
baue und dann ausführe.
@josias-r Danke! Haben Sie versucht, fs.readdirSync
stattdessen mit einem relativen Pfad (ohne Variablen) auszuführen (nur um die Bereitstellung zu debuggen)? Ich habe festgestellt, dass das normalerweise funktioniert, und wenn ja, können Sie dieses Stück Code (nur die Datei lesen, sie nirgendwo speichern) irgendwo in einem Initialisierungsprozess schreiben ( getInitialProps
oder so), so dass die Deployment-Prozess erkennt, dass er diese Datei benötigt, und liest sie dann mit der var im eigentlichen Code/Logik weiter. Es ist nicht ordentlich, aber es funktioniert, bis dies unterstützt wird. Ich glaube, dass auch die Verwendung von __dirname
in einigen Fällen funktioniert.
@BrunoBernardino Ich konnte einen Dateibaum ausgehend vom root-relativen Pfad ./
erstellen. Was ich bekam, war das folgende JSON (ohne die aufgeführten Knotenmodule):
{
"path": "./",
"name": ".",
"type": "folder",
"children": [
{
"path": ".//.next",
"name": ".next",
"type": "folder",
"children": [
{
"path": ".//.next/serverless",
"name": "serverless",
"type": "folder",
"children": [
{
"path": ".//.next/serverless/pages",
"name": "pages",
"type": "folder",
"children": [
{
"path": ".//.next/serverless/pages/api",
"name": "api",
"type": "folder",
"children": [
{
"path": ".//.next/serverless/pages/api/posts",
"name": "posts",
"type": "folder",
"children": [
{
"path": ".//.next/serverless/pages/api/posts/[...id].js",
"name": "[...id].js",
"type": "file"
}
]
}
]
}
]
}
]
}
]
},
{
"path": ".//node_modules",
"name": "node_modules",
"type": "folder",
"children": ["alot of children here ofc"]
},
{ "path": ".//now__bridge.js", "name": "now__bridge.js", "type": "file" },
{
"path": ".//now__launcher.js",
"name": "now__launcher.js",
"type": "file"
}
]
}
Ihre JSON-Datei scheint dort zu fehlen, haben Sie versucht, sie über den Code einzubinden, wie ich oben vorgeschlagen habe? Das Hauptproblem ist, dass die Optimierungen, die das Deployment ausführt, nicht immer dynamische Pfade aufnehmen, glaube ich, also hat es in der Vergangenheit für mich funktioniert, einen statischen Pfad zu erzwingen (nicht unbedingt für den eigentlichen Code, der ausgeführt wird, aber um sicherzustellen, dass die relevanten Dateien sind inklusive). Macht das Sinn?
@BrunoBernardino Ich bin auf eine Nicht-API-Lösung import()
verwenden. Ich wollte es einfach nicht so machen, weil es hackig erscheint, aber es macht im Wesentlichen dasselbe, was mein API-Endpunkt getan hätte.
... Ich habe versucht, die Datei in den statischen Ordner zu legen, aber auch das hat nicht funktioniert. Aber ich hoffe, dass der Zugriff auf das Dateisystem in Zukunft möglich sein wird.
Ich musste auch auf hackige Lösungen zurückgreifen, aber hoffentlich wird dies bald landen und mehr Leute werden Next als produktionsbereit ansehen, da diese Anwendungsfälle "wie erwartet" unterstützt werden.
Problemumgehung, die ich verwende:
# next.config.js module.exports = { serverRuntimeConfig: { PROJECT_ROOT: __dirname } }
und an der stelle brauchst du den weg
import fs from 'fs' import path from 'path' import getConfig from 'next/config' const { serverRuntimeConfig } = getConfig() fs.readFile(path.join(serverRuntimeConfig.PROJECT_ROOT, './path/to/file.json'))
Ich weiß, dass dies nicht die Notwendigkeit löst, auf Dateien mit Pfaden relativ zur aktuellen Datei zu verweisen, aber dies löst meinen sehr verwandten Anwendungsfall (Lesen von Bilddateien aus einem
/public/images
Ordner).Toller Kerl. Hat bei mir funktioniert.
Es funktioniert perfekt bei der lokalen Entwicklung, obwohl es bei der Bereitstellung auf now
nicht zu funktionieren scheint.
ENOENT: no such file or directory, open '/zeit/41c233e5/public/images/my-image.png'
at Object.openSync (fs.js:440:3)
at Object.readFileSync (fs.js:342:35)
at getEmailImage (/var/task/.next/serverless/pages/api/contact/demo.js:123:52)
at module.exports.7gUS.__webpack_exports__.default (/var/task/.next/serverless/pages/api/contact/demo.js:419:87)
at processTicksAndRejections (internal/process/task_queues.js:93:5)
at async apiResolver (/var/task/node_modules/next/dist/next-server/server/api-utils.js:42:9) {
errno: -2,
syscall: 'open',
code: 'ENOENT',
path: '/zeit/41c233e5/public/images/my-image.png'
}
Ich verstehe, dass der öffentliche Ordner in die Route verschoben wird, also habe ich versucht, ihn zu zwingen, in der Produktion im Basisordner zu suchen, aber immer noch das gleiche Ergebnis erhalten:
ENOENT: no such file or directory, open '/zeit/5fed13e9/images/my-image.png'
at Object.openSync (fs.js:440:3)
at Object.readFileSync (fs.js:342:35)
at getEmailImage (/var/task/.next/serverless/pages/api/contact/demo.js:124:52)
at module.exports.7gUS.__webpack_exports__.default (/var/task/.next/serverless/pages/api/contact/demo.js:331:87)
at processTicksAndRejections (internal/process/task_queues.js:93:5)
at async apiResolver (/var/task/node_modules/next/dist/next-server/server/api-utils.js:42:9) {
errno: -2,
syscall: 'open',
code: 'ENOENT',
path: '/zeit/5fed13e9/images/my-image.png'
}
@PaulPCIO das Problem, das Sie dort haben, liegt daran, dass es sich nicht um eine .json
, .js
- oder .ts
Datei handelt. Die Dateien unter /public
werden auf einem CDN, aber nicht auf dem Lambda (AFAIK) "bereitgestellt". In diesem Fall benötigen Sie also entweder ein dediziertes Lambda ( @now/node
) mit includeFiles
, oder, wenn Sie nur diese einzelne Datei benötigen, konvertieren Sie sie in base64
und verwenden Sie diese als var (in einer dedizierten Datei oder nicht).
Danke @BrunoBernardino , so viel erwartet, ich werde die Methode base64
verwenden
Es ist eine Auflösung für den __dirname in der bereitgestellten Umgebung??
@NicolasHz kannst du das
@BrunoBernardino Wenn ich mir die letzten Kommentare, einschließlich meiner, _dirname
in der nächsten Konfiguration" im Deployment nicht funktioniert. Sogar mit js- und JSON-Dateien. Zumindest für die Bereitstellung von now
gilt dies wahrscheinlich nicht für benutzerdefinierte Bereitstellungen.
@BrunoBernardino Ich kann einige Variablen nicht verwenden, die auf den lokalen Pfad in der bereitgestellten Umgebung verweisen. __dirname Es ist undefiniert, sobald es bereitgestellt wurde, und ich kann keine Datei aus meinen APIs-Skripten lesen.
Verstanden @NicolasHz . Ja, Sie müssen auf eine der oben genannten Lösungen zurückgreifen, je nachdem, welche Art von Datei Sie lesen/zugreifen müssen.
Nur zur Bestätigung, dass config.js bei Bereitstellungen nicht funktioniert.
Problemumgehung, die ich verwende:
# next.config.js
module.exports = {
env: {
PROJECT_DIRNAME: __dirname,
},
}
und in der API-Definition, wo ich den Pfad benötige (allPosts-Ordner enthält alle Blogs im Markdown-Format und befindet sich im Projektstamm)
import fs from 'fs'
import { join } from 'path'
const postsDirectory = join(process.env.PROJECT_DIRNAME, 'allPosts')
Es funktioniert perfekt auf die lokale Entwicklung.
Aber es gibt diesen Fehler, wenn es jetzt an zeit bereitgestellt wird.
[POST] /api/postsApi
11:00:13:67
Status:
500
Duration:
8.1ms
Memory Used:
76 MB
ID:
kxq8t-1585546213659-5c3393750f30
User Agent:
axios/0.19.2
{
fields: [ 'title', 'date', 'slug', 'author', 'coverImage', 'excerpt' ],
page: 1
}
2020-03-30T05:30:13.688Z 572075eb-4a7a-47de-be16-072a9f7005f7 ERROR Error: ENOENT: no such file or directory, scandir '/zeit/1cc63678/allPosts'
at Object.readdirSync (fs.js:871:3)
at getPostSlugs (/var/task/.next/serverless/pages/api/postsApi.js:306:52)
at module.exports.fZHd.__webpack_exports__.default (/var/task/.next/serverless/pages/api/postsApi.js:253:86)
at apiResolver (/var/task/node_modules/next/dist/next-server/server/api-utils.js:48:15)
at processTicksAndRejections (internal/process/task_queues.js:97:5) {
errno: -2,
syscall: 'scandir',
code: 'ENOENT',
path: '/zeit/1cc63678/allPosts'
}
@sjcodebook wie @BrunoQuaresma sagte, dieser Workaround funktioniert nur lokal. Ich verwende immer noch eine separate @now/node
Bereitstellung für Lambdas, um auf das Dateisystem zuzugreifen, und rufe diese Datei über eine Anfrage von der App selbst auf (oder erzeuge ein beliebiges statisches Ergebnis, das ich vor der Bereitstellung benötige). Irgendwie verrückt, aber es funktioniert.
Hallo @BrunoBernardino... Meinst du ein separates Projekt mit einem benutzerdefinierten Knotenserver?
Ich verstehe jedoch nicht, warum es eine Einstellung für " includeFiles " gibt, wenn es dann unmöglich ist, darauf zuzugreifen 🤔
@valse es kann sich im selben Projekt befinden. Hier ist ein Ausschnitt meiner now.json
:
{
"builds": [
{
"src": "next.config.js",
"use": "@now/next"
},
{
"src": "lambdas/**/*.ts",
"use": "@now/node",
"config": {
"includeFiles": ["email-templates/**"]
}
}
],
"routes": [
{
"src": "/lambdas/(.+)",
"dest": "/lambdas/$1.ts"
}
]
}
Auf diese Weise kann ich sie wie folgt anrufen:
await ky.post(`${hostUrl}/lambdas/email?token=${someToken}`);
aus einer nächsten API-Seite, vorausgesetzt, ich habe eine lambdas/email.ts
Datei, die das Senden von E-Mails und das Lesen von Vorlagendateien wie pug
.
Ich hoffe das hilft!
Außerdem funktioniert "includeFiles" nur für @now/node
(vielleicht andere, aber nicht @now/next
)
@BrunoBernardino sieht so aus, als ob es bei Verwendung von node
Funktionen jetzt kein ESM lesen kann!
Folgendes passiert, wenn ich versuche, eine Liste von mdx-Seiten zu importieren:
Code
import { NextApiRequest, NextApiResponse } from 'next'
import { promises as fs } from 'fs'
import { join } from 'path'
const { readdir } = fs
export default async (req: NextApiRequest, res: NextApiResponse) => {
const postFiles = await readdir(join(process.cwd(), 'pages', 'blog'))
const postNames: string[] = postFiles.filter((page: string) => page !== 'index.tsx')
const posts = []
for (const post of postNames) {
const mod = await import(`../pages/blog/${post}`)
posts.push({ ...mod, link: post.slice(0, post.indexOf('.')) })
}
res.status(200).json([])
}
den fehler bekomme ich:
export const title = 'My new website!'
^^^^^^
SyntaxError: Unexpected token 'export'
@talentlessguy Ich sondern bin nur ein zufriedener Kunde. Scheint so, als ob das besser für ihren Kundensupport geeignet wäre, da ich allein in diesem Ausschnitt ein paar potenzielle Probleme sehe:
__dirname
anstelle von process.cwd()
für den Basispfad verwenden. Letzteres habe ich nicht in Lambdas verwendet, aber die anderen, daher bin ich mir nicht sicher, ob das ein Problem ist oder nichtNextApiRequest
und NextApiResponse
als Typen, aber dies sollte ab @now/node"
laufen, oder? Die Typen sollten also wie folgt importiert werden:import { NowRequest, NowResponse } from '@now/node';
pages/...
aber schließen Sie sie über includeFiles
? Wie sieht dein now.json
aus?@BrunoBernardino
Ich kann __dirname
weil es immer /
, process.cwd()
stattdessen den echten Pfad anzeigt
Ich habe deine Fixes akzeptiert und es hat funktioniert:
lambdas/posts.ts
import { NowResponse, NowRequest } from '@now/node'
import { promises as fs } from 'fs'
import { join } from 'path'
const { readdir } = fs
export default async (req: NowRequest, res: NowResponse) => {
const postFiles = await readdir(join(process.cwd(), 'pages', 'blog'))
const postNames: string[] = postFiles.filter((page: string) => page !== 'index.tsx')
const posts = []
for (const post of postNames) {
const mod = await import(`../pages/blog/${post}`)
posts.push({ ...mod, link: post.slice(0, post.indexOf('.')) })
}
res.status(200).json([])
}
jetzt.json
{
"builds": [
{
"src": "next.config.js",
"use": "@now/next"
},
{
"src": "lambdas/**/*.ts",
"use": "@now/node",
"config": {
"includeFiles": ["pages/blog/*.mdx"]
}
}
],
"routes": [
{
"src": "/lambdas/(.+)",
"dest": "/lambdas/$1.ts"
}
]
}
Fehler bekomme ich:
import Meta from '../../components/Article/Meta.tsx'
^^^^^^
SyntaxError: Cannot use import statement outside a module
Sieht so aus, als ob die Typoskript-Knotenfunktion .mdx
als Modul behandeln kann:(
Alles klar, Sie haben das Problem anscheinend gefunden. Versuchen Sie, den Dateiinhalt zu lesen und zu analysieren, anstatt ihn direkt zu importieren. Ich habe noch nie gesehen, dass ein Import wie dieser funktioniert, und es scheint etwas zu sein, das nur mit etwas Babel-Magie funktionieren würde, die Sie auch gerne anstelle von einfachem TS verwenden können.
@BrunoBernardino du hast recht, aber es ist nicht klar ... Ich habe das Ziel auf esnext und das Modul auf esnext gesetzt, es sollte in der Lage sein, alles zu importieren ... aber irgendwie tut es das nicht
Es hat sowieso nichts mit dem Problem zu tun, werde es irgendwo googeln
Kein Problem. Ein paar Tipps könnten in https://mdxjs.com/advanced/typescript und https://mdxjs.com/getting-started/webpack enthalten sein, wodurch die Bereitstellung von @now/node
möglicherweise angepasst werden muss benutze es. Jedenfalls sollte ihre Unterstützung hilfreich sein.
irgendeine Bewegung dazu? Es wäre großartig, HTML-E-Mail-Vorlagen für die Verwendung in API-Routen einbinden zu können. Im Moment füge ich sie in JS-Dateien ein, aber ich bin kein besonderer Fan dieses Hacks.
Ein weiterer Hack besteht darin, den webpack raw-loader zu verwenden, um sie in js einzubetten.
yarn add --dev raw-loader
const templates = {
verify: require("raw-loader!../template/email/verify.hbs").default,
};
Verwenden Sie dann templates.verify
als Zeichenfolge.
Es gibt ein Problem mit next-i18next, das mit diesem zusammenzuhängen scheint ( vercel/vercel#4271 ). Grundsätzlich fügt now
die .json
Dateien, die sich in /public/static/locales/
in die serverlose Funktion ein. Kann jemand eine Problemumgehung bereitstellen, bis die hier besprochene Funktion als nächstes hinzugefügt wird?
@borispoehland haben Sie die oben genannten Import-/
@borispoehland haben Sie die oben genannten Import-/
@BrunoBernardino Ich weiß nicht, welchen genauen Kommentar du meinst.
Können Sie mir ein Beispiel geben, wie alle .json
Dateien in public/static/locales
irgendwie in die serverlose Funktion importiert werden können? Und wo geht das (in welcher Datei)?
Ich verwende next (wie Sie bereits erwähnt haben, ist includeFiles
nicht mit @now/next
kompatibel, idk, wenn dies Auswirkungen auf mein Problem hat).
Außerdem, weil next-i18next
für mich eine Art Blackbox ist (daher möchte ich die Dateien nicht von dort importieren), suche ich nach einer Möglichkeit, sie vollständig zu importieren, damit next-i18next
dies direkt kann darauf zugreifen (in anderen Kommentaren oben wurde manchmal nur das PROJECT_DIRNAME
innerhalb des next.config.json
und der Import musste manuell durchgeführt werden. Dies versuche ich nicht zu erreichen). Wie in vercel/vercel#4271 möchte ich nur, dass now
meine .json
Dateien irgendwie in die serverlose Funktion bringt.
@borispoehland in _any_ file in pages/api
(oder das wird dort von einem aufgerufen), mach etwas wie https://github.com/vercel/next.js/issues/8251#issuecomment -544008976
Beim Importieren brauchen Sie nichts zu tun. Der Punkt ist, dass die Webpack-Vercel-Ausführungen dann sehen, dass diese Dateien eingeschlossen werden müssen, und es sollte funktionieren.
Ich hoffe das ergibt Sinn.
@borispoehland in _any_ file in
pages/api
(oder das wird dort von einem aufgerufen), mach etwas wie #8251 (Kommentar)Beim Importieren brauchen Sie nichts zu tun. Der Punkt ist, dass die Webpack-Vercel-Ausführungen dann sehen, dass diese Dateien eingeschlossen werden müssen, und es sollte funktionieren.
Ich hoffe das ergibt Sinn.
@BrunoBernardino Das Problem bei diesem Ansatz ist, dass ich viele JSON-Dateien habe. Den Import für jede Datei manuell durchzuführen ist etwas umständlich. Gibt es eine einfachere Möglichkeit, now
zu sagen: "Hey, bitte alle JSON-Dateien in diesem Verzeichnis rekursiv abholen"? Danke im Voraus
Bearbeiten: Sogar das manuelle Importieren von json
Dateien führt zu demselben Fehler wie zuvor. Ich werde dafür ein neues Thema eröffnen, denke ich
Ich habe ein neues Thema für mein Problem eröffnet , falls jemand Interesse hat, an der Diskussion teilzunehmen. Danke fürs Erste ,
Eine andere Option / Problemumgehung, um die Möglichkeit zu aktivieren, __dirname
wie Sie es normalerweise erwarten würden, besteht darin, die Webpack-Konfiguration anzupassen.
Standardmäßig wird Webpack verschiedene Node-Globals mit Polyfills aliasen, es sei denn, Sie weisen es an, dies nicht zu tun:
https://webpack.js.org/configuration/node/
Und die Webpack-Standardeinstellungen sind, __dirname
und __filename
Ruhe zu lassen, dh sie nicht polyfill zu lassen und sie von Node wie gewohnt behandeln zu lassen.
Die Webpack-Konfiguration von Next.js verwendet/reflektiert jedoch nicht die Webpack-Standardeinstellungen https://github.com/vercel/next.js/blob/bb6ae2648ddfb65a810edf6ff90a86201d52320c/packages/next/build/webpack-config.ts#L661 -L663
Alles in allem habe ich das folgende benutzerdefinierte Next-Config-Plugin verwendet, um die Webpack-Konfiguration anzupassen.
WICHTIG: Dies funktioniert für meinen Anwendungsfall. Es wurde weder in einer Vielzahl von Umgebungen/Konfigurationen noch mit allen Next.js-Unit-/Integrationstests getestet. Die Verwendung kann unbeabsichtigte Nebenwirkungen in Ihrer Umgebung haben.
Außerdem kann Next bestimmte Gründe haben, die Webpack-Standardeinstellungen für__dirname
und__filename
. Auch hier kann der folgende Code unbeabsichtigte Nebenwirkungen haben und sollte mit Vorsicht verwendet werden.
Außerdem wurde das folgende Plugin für die Verwendung mit dem next-compose-plugins
Paket entwickelt: https://github.com/cyrilwanner/next-compose-plugins
Sollte aber auch als normales Plugin funktionieren: https://nextjs.org/docs/api-reference/next.config.js/custom-webpack-config
const withCustomWebpack = (nextCfg) => {
return Object.assign({}, nextCfg, {
webpack(webpackConfig, options) {
// We only want to change the `server` webpack config.
if (options.isServer) {
// set `__dirname: false` and/or `__filename: false` here to align with webpack defaults:
// https://webpack.js.org/configuration/node/
Object.assign(webpackConfig.node, { __dirname: false });
}
if (typeof nextCfg.webpack === 'function') {
return nextCfg.webpack(webpackConfig, options);
}
return webpackConfig;
},
});
};
Ich habe die Lösung von @jkjustjoshing implementiert , und obwohl sie lokal hervorragend funktioniert, funktioniert sie nicht, wenn ich die App in Vercel bereitstelle.
Ich bekomme folgenden Fehler:
Error: GraphQL error: ENOENT: no such file or directory, open '/vercel/37166432/public/ts-data.csv'
Mein Code:
const content = await fs.readFile(
path.join(serverRuntimeConfig.PROJECT_ROOT, "./public/ts-data.csv")
);
Hier ein Link zur Datei: https://github.com/bengrunfeld/trend-viewer/blob/master/pages/api/graphql-data.js
@bengrunfeld ja, deine Lösung funktioniert nur lokal.
Ich hatte in letzter Zeit ein ähnliches Problem (wollte eine Datei in einer API-Route lesen) und die Lösung war einfacher als erwartet.
Versuchen Sie es mit path.resolve('./public/ts-data.csv')
@borispoehland Vielen Dank!! Deine Lösung hat wunderbar funktioniert!
@bengrunfeld kein Problem, ich habe es auch durch Zufall herausgefunden ( @BrunoBernardino ;)). Es ist die Lösung für jedermanns Problem hier, denke ich.
Bitte beachten Sie, dass Sie immer noch next.config.js
festlegen müssen. Ich habe die Datei gelöscht, nachdem ich gesehen habe, dass die Lösung von @borispoehland funktioniert, und einen ähnlichen Fehler erhalten.
Dann habe ich es auf die obige Lösung von
# next.config.js
module.exports = {
serverRuntimeConfig: {
PROJECT_ROOT: __dirname
}
}
Bitte beachten Sie, dass Sie immer noch
next.config.js
festlegen müssen. Ich habe es entfernt, nachdem ich gesehen habe, dass die Lösung von @borispoehland funktioniert, und einen ähnlichen Fehler erhalten.Ich habe es auf die obige Lösung von
# next.config.js module.exports = { serverRuntimeConfig: { PROJECT_ROOT: __dirname } }
@bengrünfeld Wirklich? Vielleicht verwenden Sie an einer anderen Stelle im Code noch den PROJECT_ROOT
Ansatz, da es in meinem Projekt ohne ihn funktioniert. Wie sieht der Fehler aus?
Wie schreibe ich readFile
Bereitstellung in Vercel ein
Demo-Repository, wo es nicht funktioniert: https://github.com/mathdroid/blog-fs-demo
@mathdroid versuche readFile
und readdir
in die Funktionen getStaticProps
bzw. getStaticPaths
, andernfalls könnte der Code im Browser ausgeführt werden.
Das Importieren von fs
sollte obendrein in Ordnung sein.
Lassen Sie uns wissen, wie das funktioniert.
@borispoehland Danke für die wunderbare Lösung. Hätte nicht erwartet, dass path.resolve()
bis /public
sowohl lokal als auch auf Vercel funktionieren würde :eyes:! Du bist mein Retter für den Tag. :+1:
@borispoehland Ich habe Ihre Lösung in einer serverlosen Funktion ausprobiert, aber immer noch:
ENOENT: keine solche Datei oder kein solches Verzeichnis, öffnen Sie '/var/task/public/posts.json'
const postsFile = resolve('./public/posts.json');
const updateCache = async (posts: IPost[]): Promise<IPost[]> => {
postCache = posts;
fs.writeFileSync(postsFile, JSON.stringify(postCache)); // <====
return postCache;
}
Ich habe es mit unserem ohne next.config.js versucht
module.exports = {
serverRuntimeConfig: {
PROJECT_ROOT: __dirname
}
}
Vielleicht funktioniert Ihre Lösung nicht auf serverlosen Funktionen?
@borispoehland Ich habe Ihre Lösung in einer serverlosen Funktion ausprobiert, aber immer noch:
ENOENT: keine solche Datei oder kein solches Verzeichnis, öffnen Sie '/var/task/public/posts.json'const postsFile = resolve('./public/posts.json'); const updateCache = async (posts: IPost[]): Promise<IPost[]> => { postCache = posts; fs.writeFileSync(postsFile, JSON.stringify(postCache)); // <==== return postCache; }
Ich habe es mit unserem ohne next.config.js versucht
module.exports = { serverRuntimeConfig: { PROJECT_ROOT: __dirname } }
Vielleicht funktioniert Ihre Lösung nicht auf serverlosen Funktionen?
Ich weiß nicht, warum es bei dir nicht funktioniert... Sorry
Ok, ich habe es mit @bengrunfeld- Code zum Lesen
[Fehler: EROFS: schreibgeschütztes Dateisystem, öffne '/var/task/public/posts.json']
Also keine Möglichkeit, einen Cache zu aktualisieren, um zu viele Datenbankaufrufe zu vermeiden :(
@neckaros haben Sie versucht, meinen Ansatz zu verwenden, um eine andere Datei als .json
zu lesen, z. B. eine .jpg
Datei?
Ok, ich habe es mit @bengrunfeld- Code zum Lesen
[Fehler: EROFS: schreibgeschütztes Dateisystem, öffne '/var/task/public/posts.json']
Also keine Möglichkeit, einen Cache zu aktualisieren, um zu viele Datenbankaufrufe zu vermeiden :(
@neckaros Sie sollten in der Lage sein, von S3 (oder einem anderen externen Dateisystem) zu schreiben und zu lesen, aber ich verwende normalerweise Redis für schnelle, zwischengespeicherte Dinge, die flüchtig sein können. https://redislabs.com hält es "serverlos", und ich habe produktionsbereite Codebeispiele in https://nextjs-boilerplates.brn.sh, wenn Sie möchten.
@borispoehland ich konnte lesen, aber nicht schreiben von der dienstlosen Funktion. Aber am Ende funktionierte es, indem ich meinen Cache in den inkrementellen Builds aktualisierte (erneut validierte), anstatt neuen Inhalt hinzuzufügen. Was meiner Meinung nach kein schlechtes Muster ist. Danke für Ihre Hilfe!
@BrunoBernardino danke ich werde mir das mal anschauen. Ich suche wirklich nach einer völlig kostenlosen Bastlerlösung, die nicht kaputt geht, sobald Sie ein paar Benutzer haben :)
Ich suche wirklich nach einer völlig kostenlosen Bastlerlösung, die nicht kaputt geht, sobald Sie ein paar Benutzer haben :)
Kopiere das. RedisLabs und Vercel haben das für mich getan. 💯
Nach einigem Suchen habe ich das Schreiben von Dateien mit dem erweiterten Betriebssystempaket geschafft ...
import { tmpdir } from "os";
const doc = new PDFDocument()
const pdfPath = path.join(tmpdir(), `${store.id}${moment().format('YYYYMMDD')}.pdf`)
const writeStream = doc.pipe(fs.createWriteStream(pdfPath)
Das Lesen einer Datei funktioniert mit der @subwaymatch- Lösung
const logoPath = path.resolve('./public/logo.png')
Nach einigem Suchen habe ich das Schreiben von Dateien mit dem erweiterten Betriebssystempaket geschafft ...
import { tmpdir } from "os"; const doc = new PDFDocument() const pdfPath = path.join(tmpdir(), `${store.id}${moment().format('YYYYMMDD')}.pdf`) const writeStream = doc.pipe(fs.createWriteStream(pdfPath)
Das Lesen einer Datei funktioniert mit der @subwaymatch- Lösung
const logoPath = path.resolve('./public/logo.png')
Schön, können Sie den Inhalt dieser Datei zurücklesen? Ist das Verzeichnis zugänglich und dauerhaft?
@marklundin Mit einer Funktion namens tmpdir
bezweifle ich, dass sie dauerhaft ist, aber wenn das funktioniert, wäre es gut zu wissen, wie temporär tmpdir
tatsächlich ist, ja...
Irgendwelche Updates dazu? Ich frage mich, warum es in getInitialProps funktioniert, aber nicht in API-Routen 🤷♂️
Mein aktueller Workaround
const data = await import(`../../../../data/de/my-nice-file.json`);
res.json(data.default);
habe dieses Problem derzeit auch in API-Routen
habe dieses Problem derzeit auch in API-Routen
Hier gibt es ein paar funktionierende Lösungen, welches Problem haben Sie konkret?
Ich habe Mühe, dies trotz Vorschlägen aus diesem Thread zum Laufen zu bringen. Mein Anwendungsfall ist, dass ich eine Anleitung schreibe und den Quellcode für die Komponente neben der Komponente selbst anzeigen möchte. Meine Methode dafür besteht darin, fs zu verwenden, um die jsx-Datei der Komponente in getServerSideProps zu laden und den Zeichenfolgenwert des Dateiinhalts als Requisite zu übergeben.
Ich war überglücklich darüber, dass es vor Ort funktioniert, aber als ich es dann einsetzen wollte, war die Freude verflogen :(
Siehe: https://github.com/ElGoorf/i18next-guide/blob/fix-example-components/pages/plurals.jsx
@ElGoorf Ihr Problem ist, dass sich die public
Dateien an einem Rand befinden und die Funktionen sich auf einem Lambda befinden. @vercel/next
lässt includeFiles
@vercel/next
immer noch nicht zu, also wäre der einfachste Weg, um es zum Laufen zu bringen, eine lambda
Funktion zu verwenden.
Hier ist ein Beispielcode, der anderen hier geholfen hat: https://github.com/vercel/next.js/issues/8251#issuecomment -614220305
Danke @BrunoBernardino Ich wusste nicht, dass ich das "x versteckte Elemente laden mehr ..." verpasst hatte und dachte, ich würde verrückt werden,
Leider hatte ich mit Ihrer Lösung zu kämpfen, da ich zum ersten Mal von Edge/Lambda höre. Ich fand jedoch, @balthild näher an dem war, was ich ursprünglich gesucht hatte, bevor ich die node.fs-Methode ausprobierte: https:/ /github.com/vercel/next.js/issues/8251#issuecomment -634829189
Groß! Hast du es zum Laufen gebracht? Oder hast du immer noch Probleme?
Ich bin mir nicht sicher, ob Vercel diese Terminologie überhaupt verwendet, aber mit Edge meine ich CDN, von dem aus statische Dateien bereitgestellt werden, und mit Lambda meine ich die "Backend" -Funktionen, die von den API-Routen aufgerufen werden, die wie AWS Lambda-Funktionen isoliert sind .
Hey,
Gibt es ein Update zum Schreiben in Dateien mit next.js auf Vercel? Ich kann ohne Probleme lesen. Verwenden der const logoPath = path.resolve('./public/logo.png')
Ich versuche, die Datei public/sitemap.xml zu überschreiben (aufgrund der Größenbeschränkungen von Vercel). Ich kann sie nur fehlerfrei als statische Datei im öffentlichen Ordner zurückgeben. Ich habe die Sitemap zuvor mit zlib implementiert und die Antwort gestreamt, aber es scheint zu warten, bis der Stream fertig ist, und gibt sie dann zurück. Dies trifft nicht auf den Größenbeschränkungsfehler, ist aber leider sehr langsam. Ich bin für alle Vorschläge offen, die Leute haben könnten. Die Sitemap wird aus einem API-Aufruf an ein separates Backend erstellt und muss regelmäßig aktualisiert werden.
Dinge, die ich versucht habe:
Hey @emomooney , ich kann mir nicht vorstellen, dass Vercel jemals erlaubt, Dateien in eine Funktion zu schreiben (auch nicht zum Caching), da der Hauptvorteil von Serverless seine Zustandslosigkeit ist, und das würde ihm einen Zustand hinzufügen müssen dafür Edge/cdn verwenden.
Ich habe die Sitemap zuvor mit zlib implementiert und die Antwort gestreamt, aber es scheint zu warten, bis der Stream fertig ist, und gibt sie dann zurück.
Ich bin gespannt, ob Sie diese Langsamkeit nur bei nachfolgenden Anrufen oder nur beim ersten beim Kaltstart erlebt haben? Ich kann mir vorstellen, dass dies ein API-Aufruf an Vercel über eine next.js-API-Funktion oder ein dediziertes Lambda war, ähnlich wie ich es hier mache .
Wenn es war und es immer noch zu langsam war, ist Ihr "separates Backend" außerhalb von Vercel? Wenn ja, können Sie es möglicherweise verwenden, um eine sitemap.xml
Datei und vercel --prod
in eine Domäne zu erstellen das robots.txt
um die Sitemap mit einer anderen Domain/Subdomain zu verknüpfen.
Hilfreichster Kommentar
Problemumgehung, die ich verwende:
und an der stelle brauchst du den weg
Ich weiß, dass dies nicht die Notwendigkeit löst, auf Dateien mit Pfaden relativ zur aktuellen Datei zu verweisen, aber dies löst meinen sehr verwandten Anwendungsfall (Lesen von Bilddateien aus einem
/public/images
Ordner).