#Is there any way to include downloadable assets in content collections?

13 messages · Page 1 of 1 (latest)

summer bluff
#

I'm currently looking into migrating away from Gatsby. I have a Meeting content collection type which can have some downloadable files specified in the frontmatter. For example, the slides field for PDF files.

File structure:

content/
├─ meetings/
│  ├─ 2023-07-27/
│  │  ├─ index.md
│  │  ├─ cover.png
│  │  ├─ slides.pdf  <-- I want to make this available in build output

Markdown frontmatter:

---
title: "Introduction"
date: 2023-07-27
presenters:
- Author 1
- Author 2
cover: ./cover.png
slides: ./slides.pdf
---

The general solution seems to just be "use the public folder," but there are a few issues with this:

  1. I would have to duplicate folder structure between public and content.
  2. Image assets are co-located with the markdown file in content in order to take advantage of Astro's image optimization/schema validation. Decap CMS only supports one media directory, so if I change it to public, I would lose any benefits for images.

I find it strange that Astro allows image assets to be co-located with content collection MD files (and copies them into build output) but not any other types of files.

summer ginkgo
#

Hi @summer bluff,
i was in a similar situation with Decap and PDFs. I got somehing woking like this:

  • all Decap assets are placed in src to enjoy Astro optimization, as you did
  • using a package called fs-extra, I wrote a script that copies all PDF into dist
    The script lives in public
import fs from 'fs-extra';
import path from 'path';

async function copyPDFs(srcDir, destDir) {
  await fs.ensureDir(destDir);
  const files = await fs.readdir(srcDir);

  for (const file of files) {
    const filePath = path.join(srcDir, file);
    const fileStat = await fs.stat(filePath);

    if (fileStat.isDirectory()) {
      await copyPDFs(filePath, path.join(destDir, file));
    } else if (path.extname(file) === '.pdf') {
      const destFilePath = path.join(destDir, file);
      const fileExists = await fs.pathExists(destFilePath);

      if (!fileExists) {
        await fs.copy(filePath, destFilePath);
      } else {
        console.log(`File ${destFilePath} already exists, skipping.`);
      }
    }
  }
}

const srcDir = path.resolve('src/assets/uploads');
const destDir = path.resolve('dist/assets/uploads');

copyPDFs(srcDir, destDir)
  .then(() => console.log('PDFs copied successfully!'))
  .catch(err => console.error('Error copying PDFs:', err));

  • I updated the build command in package.json to: "build": "astro build && node public/assets/js/copy-pdfs.js",
#

There might be a better solution, but it works for me.

silk spear
summer bluff
summer bluff
silk spear
#

You can use "hybrid" mode and still have everything static.

daring island
#

you can do this in mdx files in content collections:

import someFile from './file.zip'

<a href={someFile}>Download</a>
#

you have to tell vite that zip files are assets in your config

#
export default defineConfig({
  vite: {
    assetsInclude: ["**/*.zip"]
#

I have this working with zip, pdf, and mp3 files

#

I wish there was a better overall story for page-adjacent assets though that didn't involve /public, I still run into issues with things like wasm

#

I don't think there's a way to do this via frontmatter references like you can with images