Existen múltiples formas de gestionar las imágenes y datos de nuestros proyectos.
En mi caso he elegido Strapi ya que ofrece muchísimas ventajas y comodidad para el manejo de persistencia de nuestro proyecto.
Adjunto una guía de cómo implementar Strapi en nuestro proyecto creada por nuestro amigo MidudevPreámbulo
En esta guía mostraré cómo podemos almacenar las imágenes de nuestro proyecto usando Strapi junto con un Bucket de minIO gratuito.
Para conseguir un Bucket de minIO gratuito con +80GB de almacenamiento puedes echarle un vistazo a mi post Como desplegué todos mis proyectos en servidores de 24GB Ram 4CPU gratis
Utilizando esta estructura nos aseguramos de no tener que almacenar las imágenes en el mismo proyecto ocupando un espacio valioso, que se traduce en mayor consumo de recursos en nuestro servidor, así como un despliegue mucho más lento.
Desacoplar los archivos multimedia a un Bucket de minIO nos permite una amplia escalabilidad en nuestro proyecto. Es casi de obligatorio uso cuando necesitamos crear cientos o miles de entidades con imágenes o vídeos asociados.
La estructura que queremos conseguir sería algo así:
Necesitaremos como requisito tener desplegado minIO, Strapi y nuestro front end.
Configuración necesaria
Instalamos strapi-provider-upload-ts-minio
npm install strapi-provider-upload-ts-minio
Añadimos a .env:
MINIO_ACCESS_KEY=<Clave minio>
MINIO_SECRET_KEY=<Clave secreta minio>
MINIO_ENDPOINT=<minio.example.com>
MINIO_BUCKET=<nombre del bucket de minio>
Añadimos a nuestro proyecto de strapi en: src/config/plugins.ts:
export default ({ (parameter) env: (envKey: string) => stringenv }:{(property) env: (envKey: string) => stringenv: ((parameter) envKey: stringenvKey: string ) => string}) => ({
(property) upload: {
config: {
provider: string;
providerOptions: {
accessKey: string;
secretKey: string;
bucket: string;
endPoint: string;
};
};
}upload: {
(property) config: {
provider: string;
providerOptions: {
accessKey: string;
secretKey: string;
bucket: string;
endPoint: string;
};
}config: {
(property) provider: stringprovider: 'strapi-provider-upload-ts-minio',
(property) providerOptions: {
accessKey: string;
secretKey: string;
bucket: string;
endPoint: string;
}providerOptions: {
(property) accessKey: stringaccessKey: (parameter) env: (envKey: string) => stringenv('MINIO_ACCESS_KEY'),
(property) secretKey: stringsecretKey: (parameter) env: (envKey: string) => stringenv('MINIO_SECRET_KEY'),
(property) bucket: stringbucket: (parameter) env: (envKey: string) => stringenv('MINIO_BUCKET'),
(property) endPoint: stringendPoint: (parameter) env: (envKey: string) => stringenv('MINIO_ENDPOINT'),
},
},
},
});
Si hemos configurado todo correctamente podremos ver que al subir la imagen automáticamente se sube a minIO
Y para apificarlo simplemente añadimos un tipo "media" a nuestra entidad.
Seleccionamos la imagen de la galería:
Y podemos utilizarla de esta forma:
import (alias) namespace React
import ReactReact from "react";
export const const Image: () => React.JSX.ElementImage = () => {
const [const postImage: stringpostImage, const setPostImage: React.Dispatch<React.SetStateAction<string>>setPostImage] = (alias) namespace React
import ReactReact.function React.useState<string>(initialState: string | (() => string)): [string, React.Dispatch<React.SetStateAction<string>>] (+1 overload)Returns a stateful value, and a function to update it.useState("");
type type Post = {
attributes: {
title: string;
featuredImage: {
data: {
attributes: {
url: string;
};
};
};
};
}Post = {
(property) attributes: {
title: string;
featuredImage: {
data: {
attributes: {
url: string;
};
};
};
}attributes: {
(property) title: stringtitle: string;
(property) featuredImage: {
data: {
attributes: {
url: string;
};
};
}featuredImage: {
(property) data: {
attributes: {
url: string;
};
}data: {
(property) attributes: {
url: string;
}attributes: {
(property) url: stringurl: string;
};
};
};
};
};
let let url: stringurl =
"https://mistrapi.com/api/posts?populate[featuredImage][fields][0]=url";
function fetch(input: RequestInfo | URL, init?: RequestInit | undefined): Promise<Response>[MDN Reference](https://developer.mozilla.org/docs/Web/API/fetch)fetch(let url: stringurl)
.(method) Promise<Response>.then<any, never>(onfulfilled?: ((value: Response) => any) | null | undefined, onrejected?: ((reason: any) => PromiseLike<never>) | null | undefined): Promise<any>Attaches callbacks for the resolution and/or rejection of the Promise.then(((parameter) response: Responseresponse) => (parameter) response: Responseresponse.(method) Body.json(): Promise<any>[MDN Reference](https://developer.mozilla.org/docs/Web/API/Request/json)json())
.(method) Promise<any>.then<void, never>(onfulfilled?: ((value: any) => void | PromiseLike<void>) | null | undefined, onrejected?: ((reason: any) => PromiseLike<never>) | null | undefined): Promise<...>Attaches callbacks for the resolution and/or rejection of the Promise.then(((parameter) rawPosts: anyrawPosts) => {
const const posts: Post[]posts: type Post = {
attributes: {
title: string;
featuredImage: {
data: {
attributes: {
url: string;
};
};
};
};
}Post[] = (parameter) rawPosts: anyrawPosts.anydata;
const const firstPost: PostfirstPost = const posts: Post[]posts[0];
const { const featuredImage: {
data: {
attributes: {
url: string;
};
};
}featuredImage, const title: stringtitle } = const firstPost: PostfirstPost.(property) attributes: {
title: string;
featuredImage: {
data: {
attributes: {
url: string;
};
};
};
}attributes;
const const postImage: stringpostImage = const featuredImage: {
data: {
attributes: {
url: string;
};
};
}featuredImage?.(property) data: {
attributes: {
url: string;
};
}data?.(property) attributes: {
url: string;
}attributes.(property) url: stringurl;
if (const postImage: stringpostImage) {
const setPostImage: (value: React.SetStateAction<string>) => voidsetPostImage(const postImage: stringpostImage);
}
});
return <(property) JSX.IntrinsicElements.img: React.DetailedHTMLProps<React.ImgHTMLAttributes<HTMLImageElement>, HTMLImageElement>img (property) React.ImgHTMLAttributes<HTMLImageElement>.src?: string | undefinedsrc={const postImage: stringpostImage} />;
};