#Serving images from .output/public

1 messages · Page 1 of 1 (latest)

fierce quiver
#

My app is small so I hoped that there is no need for media database. Users are able to upload photos to .output/public/images (via POST api) and get url addresses of files in the folder (via GET api).

But in production, the browser don't not load those photos, that were user-uploaded after the build. Any recommendations?

#

Client-side rendered app

snow zodiac
fierce quiver
snow zodiac
#

i always use S3/cloudfront for media uploads

#

you're putting in the public folder so that you can serve the files?

fierce quiver
#

The app needs to have the latency as low as possible and there also is no need to use cloud, we could host it all by ourselves. But agree, if it was my personal and bigger project, I would use that too

snow zodiac
#

your latency will almost always be lower if served through cloudfront

#

otherwise your app will be doing app functions and serving files

#

but, that's not what you're asking about 🙂

fierce quiver
#

Yes. It is basically a system for Signage display. One page for managing the content (also for uploading / managing the photos) and another as output which runs on the TV. That is, no serving over internet

snow zodiac
#

ha i am very familiar

#

acutely actually 🙂

#
export default defineNuxtConfig({
  devtools: { enabled: true },
  nitro:{
    publicAssets: [
      {
        baseURL: "uploads",
        dir: process.env.NUXT_IMAGES_DIR || "/var/www/apps/images",
        maxAge: 60 * 60 * 24 * 7, // 7 days
      },
    ],
  }
})
#

you can specify absolute paths outside of your build

fierce quiver
#

And what is your solution? What I proposed works perfectly when run in dev mode via npm run dev and with sufficient logic and security checks (for re-writin the file) its fune. But the problem is in production mode after the build, thats when the photos stop loading.

snow zodiac
#

but it looks like it might have the same problem

#

😄

#

let me see what i can figure out

#

this worked for me in dev and after build:

export default defineNuxtConfig({
  devtools: { enabled: true },
  nitro:{
    publicAssets: [
      {
        baseURL: "uploads",
        dir: "../uploads",
        maxAge: 60 * 60 * 24 * 7, // 7 days
      },
    ],
  }
})
fierce quiver
#

The uploads folder is in the root of the app or in the public folder?

#

I keep the photos in public/images up until now

snow zodiac
#

well, you said you were keeping them in .output/public

#

uploads would be in the same folder you would run node .output/server/index.mjs from

#

so the parent folder of .output

fierce quiver
#

Hmm, it works in dev. But after building the app, it stops showing me the new files added to the folder after the build. Basically the same behaviour as with the public/images folder.

With the new files (for example http://localhost:3000/uploads/IMG_2762.jpg I get 404 error)

snow zodiac
#

i didn't have that experience, are you sure the upload is being put in the right spot

#

i was able to place files in the upload directory after building, while running and the files showed

fierce quiver
#

I copied the file there manually, didn't want to edit the apis yet. but the effect should be the same

snow zodiac
#

yeah, i did the same thing

#

but it worked on my end

fierce quiver
#

Hm. I switched to SSR and get the error on client:

Cannot find static asset /uploads/IMG_2762.jpg

and on the server:
[Vue Router warn]: No match found for location with path "/uploads/IMG_2762.jpg"

#

Here (https://nitro.unjs.io/guide/assets) they say

When building with Nitro, it will copy the public/ directory to .output/public/ and create a manifest with metadata:
...
This allows Nitro to know the public assets without scanning the directory, giving high performance with caching headers.

It looks like nitro wont serve me any new assetts that are not in the manifest file with all the routes (and which are already cached).

Nitro handles assets via the public/ directory.

snow zodiac
#

yes which is why i tried the additional publicAssets config

#

let me try another example

fierce quiver
snow zodiac
#

so weird.. it totally worked before lol

#

i even cleared cache and restarted the process to make sure because i didn't believe it

fierce quiver
snow zodiac
#

it's copying the additional directory to the output folder lol

fierce quiver
#

yep

#

Looks like I indeed need to use some database...

snow zodiac
#

idk what i did before that made me think it was working

snow zodiac
#

that works, doesn't use the public dir stuff

fierce quiver
#

Cool, looks great. I was also thinking about doing it the api-like way, but then someone said that it didn't work for him. Thanks though, will try it tommorow and do it this way eventualy

#

Also, as you said you also do signage displays, how would you implement a loop of components?

I do it this way in <script>:

// EDU
if (infoBoard.value.edu.visible) {
    screens.push({
        component: ScreenDetail,
        props: {
            categories: infoBoard.value.edu.data.categories,
            tags: infoBoard.value.edu.data.tags,
            title: infoBoard.value.edu.data.title,
            content: infoBoard.value.edu.data.content,
            photo: infoBoard.value.edu.data.photo,
            qr: infoBoard.value.edu.data.qr,
        },
    });
}

// BLOOMBERG
if (infoBoard.value.bloomberg.visible) {
    screens.push({
        component: ScreenDetail,
        props: {
            categories: infoBoard.value.bloomberg.data.categories,
            tags: infoBoard.value.bloomberg.data.tags,
            title: infoBoard.value.bloomberg.data.title,
            content: infoBoard.value.bloomberg.data.content,
            photo: infoBoard.value.bloomberg.data.photo,
            qr: infoBoard.value.bloomberg.data.qr,
        },
    });
}

And than in template:

<div id="canvas">
    <KeepAlive>
        <component
            :is="currentScreen.component"
            :props="currentScreen.props"
        />
    </KeepAlive>
</div>

where

const index = ref(0);
const currentScreen = computed(() => screens[index.value]);

there is loop on the index.

My thing is that I'm worried of memory leaks when some screen is no longer used in the loop, it will still be loaded in the browser right? Is there any way of destroying the component when it's not used in the array anymore?

And primary loads, there is a slight glitch before the photo loads for the first time in the <KeepAlive>. Is there a way to pre-load it?

snow zodiac
#

i would likely keep them all loaded and show/hide

#

way less likely to have a leak if you aren't recreating the components every time

#

but idk much about your implementation

#

are you using puppeteer?

#

or is this remotely displayed

#

if worried about memory leaks in the browser you can always force a reload every 2-3 hours or something

fierce quiver
#

The components are created only once and than only showed... Show/hide approach with position absolute one on another is the second approach... some in animations would be harder though...

fierce quiver
snow zodiac
#

if you are serving the app from the same machine you could use puppeteer to launch the browser, and then you can monitor the memory usage, restart it, etc

fierce quiver
#

I see, nope, I dont run it on the same machhine. But could theoretically

snow zodiac
#

or you could have a separate app that runs connected to the display, idk i use to make all sorts of stuff like this