Next.js: Маршруты API Next.js (и страницы) должны поддерживать чтение файлов.

Созданный на 5 авг. 2019  ·  87Комментарии  ·  Источник: vercel/next.js

Запрос функции

Ваш запрос функции связан с проблемой? Пожалуйста, опишите.

В настоящее время невозможно читать файлы из маршрутов или страниц API.

Опишите желаемое решение

Я хочу иметь возможность вызывать fs.readFile с помощью пути __dirname и "просто работать".

Это должно работать в режимах разработки и производства.

Опишите альтернативы, которые вы рассмотрели

Это может потребовать некоторой интеграции с @zeit/webpack-asset-relocator-loader . Этот плагин обрабатывает такие типы требований.

Однако в этом нет необходимости. Я был бы в порядке с тем, что _only_ работает с __dirname и __filename (без относительных или основанных на cwd путей).

Дополнительный контекст

Пример:

// 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'
  )
  // ...
}

Примечание: я знаю, что вы можете обмануть приведенный выше пример ☝️ с помощью require , но дело не в этом. 😄

story feature request

Самый полезный комментарий

Обходной путь, который я использую:

# next.config.js
module.exports = {
    serverRuntimeConfig: {
        PROJECT_ROOT: __dirname
    }
}

и в нужном месте путь

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'))

Я знаю, что это не решает необходимости ссылаться на файлы с путями относительно текущего файла, но это решает мой очень связанный вариант использования (чтение файлов изображений из папки /public/images ).

Все 87 Комментарий

Просто хотел поддержать это, пытаясь реализовать загрузку файлов с использованием маршрутов API. Я могу получить файл для загрузки, но затем мне потребуется снова получить к нему доступ, чтобы загрузить его в корзину S3.

Я поддерживаю это! Кроме того, возможность читать каталоги очень важна для использования моей компанией, поскольку мы храним наши данные, такие как члены команды и сообщения в блогах, в каталоге содержимого, поэтому мы ищем способ потребовать все файлы в каталоге.

Вышеупомянутый PR исправит это! ☝️ 🙏

Как насчет fs.writeFile , это возможно? Например, создайте и сохраните файл JSON на основе веб-перехватчика, который был размещен на /api/route

Эй, @marlonmarcello , это будет возможно. Оставайтесь с нами 😊

Это уже решено?

Еще нет, вы можете подписаться на # 8334

@ huv1k Большое спасибо!

Есть ли способ помочь этому продвинуться быстрее?

Стоит отметить: если вы используете TypeScript, вы уже можете напрямую импортировать файл JSON как модуль (убедитесь, что resolveJsonModule равно true в tsconfig.json ). Например:

import myJson from '../../../some/path/my.json';

Форма объекта JSON также автоматически используется в качестве его типа, поэтому автозаполнение действительно удобно.

Обходной путь, который я использую:

# next.config.js
module.exports = {
    serverRuntimeConfig: {
        PROJECT_ROOT: __dirname
    }
}

и в нужном месте путь

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'))

Я знаю, что это не решает необходимости ссылаться на файлы с путями относительно текущего файла, но это решает мой очень связанный вариант использования (чтение файлов изображений из папки /public/images ).

Видел, что в PR это немного изменилось - есть ли обновления о текущих планах (или нет)? Похоже, есть некоторые стратегии, которых вы не хотите придерживаться, не забудьте перечислить их + почему участники могут попробовать?

Это блокирует использование нексуса с Next.js. Было бы здорово снова сделать это приоритетным.

Обходной путь, который я использую:

# next.config.js
module.exports = {
    serverRuntimeConfig: {
        PROJECT_ROOT: __dirname
    }
}

и в нужном месте путь

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'))

Я знаю, что это не решает необходимости ссылаться на файлы с путями относительно текущего файла, но это решает мой очень связанный вариант использования (чтение файлов изображений из папки /public/images ).

Потрясаящий человек. Работал у меня.

Я использовал для этого новый метод getStaticProps (в # 9524). В настоящее время метод отмечен как нестабильный, но, похоже, команда Next.js хорошо поддерживает его официальную отправку.

например:

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 У вас есть проект с открытым исходным кодом, в котором вы это используете? Интересно, как бы это выглядело.

У проекта нет открытого исходного кода, но я рад поделиться большей частью своей конфигурации, если у вас возникнут дополнительные вопросы.

@ ScottSmith95 У меня _все_ вопросы 😛

  1. Где в вашем проекте вы храните файлы данных? (снаружи / внутри src ?)
  2. Как выглядит компонент страницы next.js, использующий их?
  3. Это только жестко заданные пути или вы можете загрузить файл на основе параметров пути?
  4. Как работает сборка / развертывание, особенно если это не жестко заданные пути?

@Svish Мы храним файлы данных в / data внутри нашего проекта. (Страницы находятся в / pages, а не в / src / prages.) Этот компонент страницы выглядит следующим образом (реквизиты отправляются в компонент Home, который является экспортом по умолчанию):

// /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>
    </>
  );
};

Для более сложных страниц с динамическими маршрутами мы получаем эти данные следующим образом:

// /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 }
  };
}

Развертывание происходит очень гладко, с динамическими маршрутами становится необходимо getStaticPaths() . Я рекомендую вам ознакомиться с документацией по этому поводу в RFC , но вот пример того, как мы справляемся с этим, собирая все данные о членах нашей команды и передавая их в Next.js.

// /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 Выглядит многообещающе! Если у вас есть время, задайте пару дополнительных вопросов:

  1. Что вы здесь делаете для создания статического сайта? Т.е. при использовании next export ?
  2. Правильно ли я понял, что getStaticPaths возвращает список параметров пути, который затем (к следующему) передается, один за другим, в getStaticProps для каждого рендеринга?
  3. Можете ли вы использовать getStaticProps без getStaticPaths , например, для страницы без каких-либо параметров?
  4. Можете ли вы использовать getStaticProps в _app ? Например, если у вас есть конфигурация для всего сайта, которую вы хотите загрузить, или что-то в этом роде?

А как насчет API ?? Эти хуки предназначены для страниц, но как насчет API?

Я запутался. Мне удалось установить _dirname как переменную env в следующей конфигурации. Поэтому я смог получить доступ к файловой системе из API, но он работал только локально. После развертывания до настоящего момента я получил ошибку. Есть идеи, почему он не работает после развертывания?

@ josias-r основная проблема обычно заключается в том, что файлы для чтения не включены в развертывание, но это зависит от того, как вы их включаете и к каким типам файлов они относятся ( js / json обычно нормально, но для других типов файлов, таких как .jade , потребуются альтернативные способы работы с ним, например, использование отдельного @now/node lambda / deployment для чтения / обработки этих файлов).

Если вы можете подробнее объяснить ошибку, возможно, кто-то сможет вам помочь.

@BrunoBernardino На самом деле речь шла о файлах JSON внутри моей папки src. Но на самом деле даже метод fs.readdirSync(my_dirname_env_var) уже терпит неудачу при развертывании. Так что после развертывания этот каталог, похоже, вообще не существует. Вот что я получаю, когда пытаюсь получить доступ к полному пути к json через мой API:

ERROR   Error: ENOENT: no such file or directory, open '/zeit/3fc37db3/src/content/somejsonfilethatexists.json'

И, как я уже упоминал, это работает локально, когда я создаю, а затем запускаю npm start .

@ josias-r Спасибо! Вы пытались вместо этого выполнить fs.readdirSync с относительным путем (без переменных) (просто для отладки развертывания)? Я обнаружил, что это обычно работает, и если да, вы можете написать этот фрагмент кода (просто читая файл, не сохраняя его нигде) где-нибудь в процессе инициализации ( getInitialProps или что-то в этом роде), чтобы процесс развертывания определяет, что ему нужен этот файл, а затем продолжает читать его с помощью var в фактическом коде / логике. Это не изящно, но работает до тех пор, пока не будет поддерживаться. Я считаю, что в некоторых случаях также работает __dirname .

@BrunoBernardino Мне удалось построить дерево файлов, начиная с корневого пути ./ . Я получил следующий JSON (без перечисленных узловых модулей):

{
  "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"
    }
  ]
}

Кажется, там отсутствует ваш файл JSON. Вы пытались включить его с помощью кода, как я предлагал выше? Основная проблема заключается в том, что оптимизации, выполняемые при развертывании, не всегда выбирают динамические пути, я считаю, поэтому принудительный статический путь работал у меня в прошлом (не обязательно для фактического выполняемого кода, но для того, чтобы убедиться, что соответствующие файлы включены). В этом есть смысл?

@BrunoBernardino Я перешел на решение без API. Поскольку я хочу динамически запрашивать файлы из папки и мне нужно только содержимое этих файлов, я могу использовать метод import() . Я просто не хотел делать это таким образом, потому что это кажется хакерским, но он, по сути, делает то же самое, что и моя конечная точка API.
... Я попытался поместить файл в статическую папку, но это тоже не сработало. Но я надеюсь, что доступ к файловой системе будет возможен в будущем.

Мне также пришлось прибегнуть к хакерским решениям, но, надеюсь, они скоро появятся, и все больше людей начнут видеть Next как готовую к производству, поскольку эти варианты использования будут поддерживаться «как ожидалось».

Обходной путь, который я использую:

# next.config.js
module.exports = {
    serverRuntimeConfig: {
        PROJECT_ROOT: __dirname
    }
}

и в нужном месте путь

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'))

Я знаю, что это не решает необходимости ссылаться на файлы с путями относительно текущего файла, но это решает мой очень связанный вариант использования (чтение файлов изображений из папки /public/images ).

Потрясаящий человек. Работал у меня.

Он отлично работает при локальной разработке, хотя, похоже, не работает при развертывании в now .

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'
}

Я понимаю, что общая папка перемещается по маршруту, поэтому я попытался заставить ее искать в базовой папке во время производства, но все равно получил тот же результат:

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 проблема, с которой вы столкнулись, .json , .js или .ts . Файлы в /public "развертываются" в CDN, но не в лямбда-выражении (AFAIK), поэтому в этом случае вам потребуется либо выделенное развертывание лямбда-выражения ( @now/node ) с includeFiles , или, если вам нужен только этот единственный файл, преобразуйте его в base64 и используйте его как переменную (в выделенном файле или нет).

Спасибо, @BrunoBernardino этого и ожидал, я буду использовать метод base64

Это какое-то разрешение __dirname в развернутой среде ??

@NicolasHz, ты можешь уточнить? Я не совсем понял ваш вопрос.

@BrunoBernardino Глядя на последние комментарии, включая мой, я почти уверен, что хак "map _dirname в следующей конфигурации" не работает при развертывании. Даже с файлами j / js и JSON. По крайней мере, для развертывания now , которое, вероятно, не учитывается для пользовательских развертываний.

@BrunoBernardino Я не могу использовать некоторые переменные, указывающие на локальный путь в развернутом окружении. __dirname он не определен после развертывания, и я не могу прочитать файл из моих сценариев apis.

Понял @NicolasHz . Да, вам нужно прибегнуть к одному из вышеперечисленных решений, в зависимости от того, к какому типу файла вам нужно прочитать / получить доступ.

Просто подтверждаю, config.js не работает с развертываниями.

Обходной путь, который я использую:

# next.config.js
module.exports = {
  env: {
    PROJECT_DIRNAME: __dirname,
  },
}

и в определении api, где мне нужен путь (папка allPosts содержит все блоги в формате уценки и находится в корне проекта)

import fs from 'fs'
import { join } from 'path'

const postsDirectory = join(process.env.PROJECT_DIRNAME, 'allPosts')

Он отлично работает над локальной разработкой.
Но теперь при развертывании на zeit выдает эту ошибку.

[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, например @BrunoQuaresma, сказал, что обходной путь работает только локально. Я все еще использую отдельное развертывание @now/node для лямбда-выражений для доступа к файловой системе и вызываю этот файл по запросу из самого приложения (или генерирую любой статический результат, который мне нужен перед развертыванием). Какое-то безумие, но это работает.

Привет, @BrunoBernardino ... Вы имеете в виду отдельный проект с настраиваемым сервером узлов?

Однако я не понимаю, зачем нужен параметр "

@valse это может быть в том же проекте. Вот фрагмент моего 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"
    }
  ]
}

Таким образом, я могу вызвать их примерно так:

await ky.post(`${hostUrl}/lambdas/email?token=${someToken}`);

изнутри следующей страницы api, предполагая, что у меня есть файл lambdas/email.ts который обрабатывает отправку электронных писем и чтение из файлов шаблонов, таких как pug .

Надеюсь, это поможет!

Кроме того, includeFiles работает только для @now/node (может быть, для других, но не для @now/next )

@BrunoBernardino выглядит так, будто при использовании node функций теперь он не может читать ESM!

вот что происходит, когда я пытаюсь импортировать список страниц mdx:

код

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([])
}

ошибка, которую я получаю:

export const title = 'My new website!'
^^^^^^

SyntaxError: Unexpected token 'export'

@talentlessguy Я не из команды Zeit / Vercel, я просто счастливый клиент. Похоже, это лучше подходит для их поддержки клиентов, поскольку я вижу несколько потенциальных проблем только из этого фрагмента:

  1. Возможно, вы захотите использовать просто ничего или __dirname вместо process.cwd() в качестве базового пути. Я не использовал последний в лямбдах, но другие, поэтому я не уверен, проблема в этом или нет.
  2. Вы импортируете NextApiRequest и NextApiResponse как типы, но это должно быть запущено из @now/node" , верно? Таким образом, типы должны быть импортированы как:
import { NowRequest, NowResponse } from '@now/node';
  1. Вы импортируете / читаете из pages/... но включаете ли вы их через includeFiles ? Как выглядит ваш now.json ?

@BrunoBernardino

Я не могу использовать __dirname потому что вместо этого всегда / , process.cwd() , показывает реальный путь

Я принял исправления ur, и это сработало:

лямбды / 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([])
}

now.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"
    }
  ]
}

ошибка я получаю:

import Meta from '../../components/Article/Meta.tsx'
^^^^^^

SyntaxError: Cannot use import statement outside a module

Похоже, функция узла машинописного текста не может обрабатывать .mdx как модуль :(

Хорошо, похоже, вы нашли проблему. Попробуйте прочитать содержимое файла и проанализировать его вместо прямого импорта. Я никогда не видел такого импорта, и похоже, что что-то, что работает только с некоторой магией Babel, которую вы также можете использовать вместо простого TS.

@BrunoBernardino, вы правы, но это не просто ts ... У меня есть цель, установленная на esnext, и модуль на esnext, он должен иметь возможность импортировать все ... но почему-то это не так

в любом случае это не связано с проблемой, собираюсь где-нибудь погуглить

Не стоит беспокоиться. Несколько советов могут быть в https://mdxjs.com/advanced/typescript и https://mdxjs.com/getting-started/webpack, которые могут сделать так, чтобы развертывание @now/node нужно было настроить, чтобы используй это. В любом случае их поддержка должна помочь.

какие-нибудь движения по этому поводу? Было бы здорово иметь возможность включать HTML-шаблоны электронной почты для использования в API Routes. Прямо сейчас я включаю их в файлы JS, но я не особо фанат этого хака.

Еще один прием - использовать raw-loader webpack для встраивания их в js.

yarn add --dev raw-loader
const templates = {
    verify: require("raw-loader!../template/email/verify.hbs").default,
};

Затем используйте templates.verify как строку.

Возникла проблема с next-i18next, которая, похоже, связана с этим ( vercel / vercel # 4271 ). Обычно now не помещает файлы .json расположенные внутри /public/static/locales/ в бессерверную функцию. Может ли кто-нибудь предложить обходной путь, пока обсуждаемая здесь функция не будет добавлена ​​в следующую?

@borispoehland пробовали ли вы обходные пути импорта / требования сверху? Это должно сработать.

@borispoehland пробовали ли вы обходные пути импорта / требования сверху? Это должно сработать.

@BrunoBernardino Я не знаю, какой именно комментарий вы имеете в виду.

Можете ли вы привести мне пример того, как каким-то образом импортировать все файлы .json внутри public/static/locales в бессерверную функцию? И где это делать (в каком файле)?

Я использую следующий (как вы заявили ранее, includeFiles несовместимо с @now/next , idk, если это повлияет на мою проблему).

Кроме того, поскольку next-i18next для меня является своего рода черным ящиком (поэтому я не хочу импортировать файлы оттуда), я ищу способ полностью импортировать их, чтобы next-i18next мог напрямую доступ к ним (в других комментариях выше, иногда только PROJECT_DIRNAME определялся внутри next.config.json и импорт приходилось выполнять вручную. Я не пытаюсь этого достичь). Как и в vercel / vercel # 4271 , я просто хочу, чтобы now каким-то образом перенес мои файлы .json в бессерверную функцию.

@borispoehland в pages/api (или который вызывается одним из них), сделайте что-то вроде https://github.com/vercel/next.js/issues/8251#issuecomment -544008976

С импортом ничего делать не нужно. Дело в том, что запускаемая версия веб-пакета затем увидит, что эти файлы необходимо включить, и он должен работать.

Я надеюсь, что в этом есть смысл.

@borispoehland в pages/api (или тот, который там вызывается ), сделайте что-нибудь вроде # 8251 (комментарий)

С импортом ничего делать не нужно. Дело в том, что запускаемая версия веб-пакета затем увидит, что эти файлы необходимо включить, и он должен работать.

Я надеюсь, что в этом есть смысл.

@BrunoBernardino проблема с этим подходом в том, что у меня много файлов json. Выполнение импорта вручную для каждого файла довольно обременительно. Есть ли более простой способ сказать now : «Эй, пожалуйста, рекурсивно извлеките все файлы json из этого каталога»? заранее спасибо

Изменить: даже ручной импорт файлов json приводит к той же ошибке, что и раньше. Я собираюсь открыть для этого новый выпуск, я думаю

Я открыл новый выпуск по своей проблеме , на случай, если кто-то захочет присоединиться к обсуждению. Спасибо, @BrunoBernardino !

Другой вариант / обходной путь, позволяющий использовать __dirname как вы обычно ожидаете, - это настроить конфигурацию webpack.

По умолчанию webpack будет связывать различные глобальные объекты Node с полифилами, если вы не укажете ему:
https://webpack.js.org/configuration/node/
И настройки по умолчанию для веб-пакета - оставить только __dirname и __filename , т.е. не заполнять их полифилом и позволить узлу обрабатывать их как обычно.

Однако конфигурация веб-пакета Next.js не использует / не отражает настройки веб-пакета по умолчанию https://github.com/vercel/next.js/blob/bb6ae2648ddfb65a810edf6ff90a86201d52320c/packages/next/build/webpack-config.ts#L661 -L663

Все это говорит о том, что я использовал нижеприведенный настраиваемый плагин конфигурации Next для настройки конфигурации webpack.

ВАЖНО: это работает для моего варианта использования. Он не тестировался в широком диапазоне сред / конфигураций и не был протестирован со всеми модульными / интеграционными тестами Next.js. Его использование может иметь непреднамеренные побочные эффекты в вашей среде.
Кроме того, у Next могут быть определенные причины не использовать настройки веб-пакета по умолчанию для __dirname и __filename . Итак, приведенный ниже код может иметь непредвиденные побочные эффекты, и его следует использовать с осторожностью.

Кроме того, указанный ниже плагин был разработан для использования с пакетом next-compose-plugins : https://github.com/cyrilwanner/next-compose-plugins

Но должен работать и как обычный плагин: 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;
    },
  });
};

Я реализовал решение с помощью @jkjustjoshing , и хотя оно отлично работает локально, оно не работает, когда я развертываю приложение на Vercel.

Я получаю следующую ошибку:

Error: GraphQL error: ENOENT: no such file or directory, open '/vercel/37166432/public/ts-data.csv'

Мой код:

const content = await fs.readFile(
  path.join(serverRuntimeConfig.PROJECT_ROOT, "./public/ts-data.csv")
);

Вот ссылка на файл: https://github.com/bengrunfeld/trend-viewer/blob/master/pages/api/graphql-data.js

@bengrunfeld да, ваше решение работает только локально.

В последнее время у меня была аналогичная проблема (хотел прочитать файл в маршруте API), и решение оказалось проще, чем ожидалось.

Попробуйте path.resolve('./public/ts-data.csv')

@borispoehland Большое вам спасибо !! Ваше решение прекрасно сработало!

@bengrunfeld без проблем, я тоже узнал это по совпадению ( @BrunoBernardino ;)). Думаю, это решение всех проблем.

Обратите внимание, что вам все равно нужно установить next.config.js . Я удалил файл после того, как увидел, что решение @borispoehland работает, и получил аналогичную ошибку.

Затем я сбросил его на решение @jkjustjoshing выше, снова развернул в Vercel, и он сработал.

# next.config.js
module.exports = {
    serverRuntimeConfig: {
        PROJECT_ROOT: __dirname
    }
}

Обратите внимание, что вам все равно нужно установить next.config.js . Я удалил его после того, как увидел, что решение @borispoehland работает, и получил аналогичную ошибку.

Я сбросил его на решение @jkjustjoshing выше, снова развернул в Vercel, и оно сработало.

# next.config.js
module.exports = {
    serverRuntimeConfig: {
        PROJECT_ROOT: __dirname
    }
}

@bengrunfeld Правда? Возможно, вы все еще используете подход PROJECT_ROOT в другом месте кода, потому что в моем проекте он работает без него. Как выглядит ошибка?

При развертывании в Vercel, как мне написать readFile на странице, которая будет работать как в SSG, так и в режиме SSR / предварительного просмотра?

Демо-репозиторий, где он не работает: https://github.com/mathdroid/blog-fs-demo

https://blog-fs-demo.vercel.app/

@mathdroid попробуйте переместить readFile и readdir внутри функций getStaticProps и getStaticPaths соответственно, иначе код может работать в браузере.

Тем не менее, импорт fs должен быть в порядке.

Сообщите нам, как это работает.

@borispoehland Спасибо за прекрасное решение. Не ожидал, что path.resolve() to /public будет работать как локально, так и на Vercel: eyes :! Ты мой спаситель на день. : +1:

@borispoehland Я пробовал ваше решение внутри бессерверной функции, но все равно получил:
ENOENT: нет такого файла или каталога, откройте '/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;
}

я пробовал с нашим без next.config.js

module.exports = {
    serverRuntimeConfig: {
        PROJECT_ROOT: __dirname
    }
}

Может ваше решение не работает на бессерверных функциях?

@borispoehland Я пробовал ваше решение внутри бессерверной функции, но все равно получил:
ENOENT: нет такого файла или каталога, откройте '/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;
}

я пробовал с нашим без next.config.js

module.exports = {
    serverRuntimeConfig: {
        PROJECT_ROOT: __dirname
    }
}

Может ваше решение не работает на бессерверных функциях?

Я не знаю, почему это не работает с твоей стороны ... Извини

Хорошо, я заставил его работать для чтения, используя код @bengrunfeld, но, к сожалению, вы не можете писать:
[Ошибка: EROFS: файловая система только для чтения, откройте '/var/task/public/posts.json']
Так что нет возможности обновить кеш, чтобы избежать слишком большого количества обращений к базе данных :(

@neckaros пробовали ли вы использовать мой подход для чтения файла, отличного от .json , например файла .jpg ?

Хорошо, я заставил его работать для чтения, используя код @bengrunfeld, но, к сожалению, вы не можете писать:

[Ошибка: EROFS: файловая система только для чтения, откройте '/var/task/public/posts.json']

Так что нет возможности обновить кеш, чтобы избежать слишком большого количества обращений к базе данных :(

@neckaros, вы должны иметь возможность писать и читать с S3 (или какой-либо другой внешней файловой системы), но я обычно использую redis для быстрых, кешированных вещей, которые могут быть нестабильными. https://redislabs.com сохраняет его "бессерверным", и у меня есть готовые к производству примеры кода в https://nextjs-boilerplates.brn.sh, если хотите.

@borispoehland я мог читать, но не мог писать из бесполезной функции. Но в итоге я заставил его работать, обновляя мой кеш в инкрементальных сборках (повторная проверка) вместо добавления нового контента. Что, я думаю, неплохой образец. Спасибо за вашу помощь!

@BrunoBernardino, спасибо, я посмотрю. На самом деле ищу полностью бесплатное решение для любителей, которое не сломается, когда у вас будет несколько пользователей :)

На самом деле ищу полностью бесплатное решение для любителей, которое не сломается, когда у вас будет несколько пользователей :)

Скопируй это. RedisLabs и Vercel сделали это за меня. 💯

После некоторого покопания я получил файлы, работающие с расширенным пакетом ОС ...

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)

чтение файла работает с решением @subwaymatch
const logoPath = path.resolve('./public/logo.png')

После некоторого покопания я получил файлы, работающие с расширенным пакетом ОС ...

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)

чтение файла работает с решением @subwaymatch
const logoPath = path.resolve('./public/logo.png')

Отлично, вы можете прочитать содержимое этого файла? Каталог доступен и постоянен?

@marklundin С функцией с именем tmpdir Я сомневаюсь, что она постоянная, но если это сработает, то было бы хорошо узнать, насколько временным на самом деле является tmpdir , да ... 🤔

Есть обновления по этому поводу? Мне интересно, почему это работает в getInitialProps, а не в маршрутах API 🤷‍♂️

Мой текущий обходной путь

const data = await import(`../../../../data/de/my-nice-file.json`);
res.json(data.default);

в настоящее время есть эта проблема и в маршрутах API

в настоящее время есть эта проблема и в маршрутах API

Здесь есть несколько рабочих решений, в чем конкретно у вас проблема?

Я изо всех сил пытаюсь заставить это работать даже с предложениями из этой ветки. Мой вариант использования: я пишу руководство и хочу показать исходный код компонента вместе с самим компонентом. Мой метод для этого - использование fs для загрузки файла jsx компонента внутри getServerSideProps и передача строкового значения содержимого файла в качестве опоры.

Я безумно думал о том, что он работает локально, но потом, когда я пошел развертывать его, радость исчезла :(

См. Https://github.com/ElGoorf/i18next-guide/blob/fix-example-components/pages/plurals.jsx

@ElGoorf ваша проблема в том, что файлы public находятся на краю, а функции находятся на лямбде. Теперь @vercel/next прежнему не позволяет использовать includeFiles , поэтому самый простой способ заставить его работать - использовать с ним функцию lambda .

Вот пример кода, который помог другим здесь: https://github.com/vercel/next.js/issues/8251#issuecomment -614220305

Спасибо @BrunoBernardino. Я не осознавал, что пропустил «x скрытых элементов загружают больше ...» и подумал, что схожу с ума от темы, теряющей смысл!

К сожалению, у меня возникли проблемы с вашим решением, поскольку я впервые слышу о Edge / Lambda, однако я обнаружил, что решение https: / /github.com/vercel/next.js/issues/8251#issuecomment -634829189

Большой! Получилось ли у вас работать? Или у вас все еще есть проблемы?

Я не уверен, что Vercel вообще использует эту терминологию, но под Edge я подразумеваю CDN, откуда отправляются статические файлы, а под лямбдой я подразумеваю «внутренние» функции, которые вызываются из маршрутов API, которые изолированы, как функции AWS Lambda. .

Привет,

Есть какие-нибудь обновления по записи в файлы с помощью next.js на Vercel? Я не умею читать. Использование const logoPath = path.resolve('./public/logo.png')

Я пытаюсь перезаписать файл public / sitemap.xml (из-за ограничений размера на vercel), я могу вернуть его без ошибок только как статический файл в общей папке. Я ранее реализовал карту сайта с помощью zlib и потоковой передачи ответа, но, похоже, он ждет завершения потока, а затем возвращает его. Это не касается ошибки ограничения размера, но, к сожалению, очень медленно. Я открыт для любых предложений, которые могут быть у людей. Карта сайта создается из вызова API к отдельной серверной части и требует регулярного обновления.

Я пытался:

  • Архивирование и потоковая передача xml - работает, но очень медленно.
  • Создание и возврат карты сайта из функции api, к сожалению, превышает ограничение по размеру.
  • Чтение статического XML-файла из общей папки (работает) независимо от размера, но не обновляется.
  • Тестовая запись в этот файл не работает. Тестовая запись в любой файл / папку не работает
  • Тест возвращает статический XML-файл из функции "api", ошибка размера. Это работает локально.
  • Тест, возвращающий статический xml-файл из ошибки размера "страницы" getServerSideProp. Это работает локально.
  • Был бы признателен за любые идеи?

Привет, @emomooney , я не представляю, что Vercel когда-либо позволит записывать файлы в функции (даже для кеширования), поскольку главное «преимущество» бессерверного режима - это отсутствие состояния, и это добавило бы к нему состояние, поэтому я думаю, что вы для этого нужно использовать edge / cdn.

Я ранее реализовал карту сайта с помощью zlib и потоковой передачи ответа, но, похоже, он ждет завершения потока, а затем возвращает его.

Мне любопытно, испытывали ли вы эту медлительность только при последующих вызовах или только при первом холодном запуске? Я предполагаю, что это был API-вызов Vercel через функцию api next.js или выделенную лямбду, похожую на то, что я делаю здесь .

Если это было и все еще было слишком медленно, находится ли ваш «отдельный сервер» за пределами Vercel? Если это так, вы можете потенциально использовать его для создания файла sitemap.xml и vercel --prod в домене, в основном "кэшируя" файл, чтобы он был доступен для чтения и доступа, и вам просто нужно обновить robots.txt чтобы связать карту сайта с другим доменом / субдоменом.

Была ли эта страница полезной?
0 / 5 - 0 рейтинги