I have a Meteor application deployed with nginx.
I try to upload images from the application to save the images on the server. When I'm in localhost, I save my images in the myapp/public/uploads folder. But, when I deploy, this folder become myapp/bundle/programs/web.browser/app/uploads. So, when I upload an image, it saved in a new folder in myapp/public/uploads. But so, I can't access to it. When I'm in localhost I access to my images like that : localhost:3000/uploads/myImage.png but when I do myAdress/uploads/myImage.png I access to the myapp/bundle/programs/web.browser/app/uploads folder and not the one where the images are saved (myapp/public/uploads).
This is my code to save images :
Meteor.startup(function () {
UploadServer.init({
tmpDir: process.env.PWD + '/app/uploads',
uploadDir: process.env.PWD + '/app/uploads',
checkCreateDirectories: true,
uploadUrl: '/upload',
// *** For renaming files on server
getFileName: function(file, formData) {
//CurrentUserId is a variable passed from publications.js
var name = file.name;
name = name.replace(/\s/g, '');
return currentTileId + "_" + name;
},
finished: function(fileInfo, formFields) {
var name = fileInfo.name;
name = name.replace(/\s/g, '');
insertionImages(name, currentTileId, docId);
},
});
});
So, do you know how can I do to save and access to my images when the application is deployed ? Maybe save the image in the myapp/bundle/programs/web.browser/app/uploads folder or access to the myapp/public/uploads folder with an url.
This is what we do.
Use an external dir for uploads, say, /var/uploads. Keeping the uploads in public folder makes the meteor app to reload in the dev environment, on any file upload.
Now, at local, use Meteor to serve these files at a certain url. In production, use nginx to serve the same at the same url.
For Development
1) Symlink your upload dir to a hidden folder in public.
eg:
ln -s /var/uploads /path/to/public/.#static
2) Serve the public hidden folder via Meteor by using:
The url /static will server the folder public/.#static by using the following code on the server. Ref: How to prevent Meteor from watching files?
var fs = require('fs'), mime = require('mime');
WebApp.rawConnectHandlers.use(function(req, res, next) {
var data, filePath, re, type;
re = /^\/static\/(.*)$/.exec(req.url);
if (re !== null) {
filePath = process.env.PWD + '/public/.#static/' + re[1];
try {
stats = fs.lstatSync(filePath);
if (stats.isFile()) {
type = mime.lookup(filePath);
data = fs.readFileSync(filePath, data);
res.writeHead(200, {
'Content-Type': type
});
res.write(data);
res.end();
}
}
catch (e) {
// console.error(filePath, "not found", e); // eslint-disable-line no-console
next();
}
}
else {
next();
}
});
For production
1) Use nginx for serving the upload dir
server {
...
location /static/ {
root /var/uploads;
}
...
}
That's it. /static will server the content of your uploads dir i.e. /var/uploads
Related
We are deploying a remote app written in NextJS and Typescript; The host app is in React only.
Currently the host app gets a 404 not found error as the remote app runs into this error in the Build Snapshot on Jenkins
+ ls ./dist/static/chunks/remoteEntry.js
ls: cannot access './dist/static/chunks/remoteEntry.js': No such file or directory
script returned exit code 2
However, the file is generated locally and both apps are able to spin up in local environment.
Here is our next.config.js:
const NextFederationPlugin = require('#module-federation/nextjs-mf');
const { exposedModules } = require('./lib/routes');
const version = process.env.VERSION_OVERRIDE || require('./package.json').version;
const deps = require('./package.json').dependencies;
// Note: This path needs to match with what's specified in CIRRUS_FRONTEND_ENTRYPOINT for www.
const assetBasePath = process.env.CDN_PATH ? `${process.env.CDN_PATH}${version}` : process.env.ASSET_BASE_PATH;
// Note: Heavily references module federation example meant for omnidirectional federation between Next apps.
// Changes mostly around path resolution due to our current resolution pattern via cdn
// https://github.com/module-federation/module-federation-examples/blob/master/nextjs/home/next.config.js
module.exports = {
webpack(config, options) {
Object.assign(config.experiments, { topLevelAwait: true });
// Integrated mode calls `next build` which has minimization by default. For local development, this is unnecessary.
if (process.env.NEXT_PUBLIC_ENVIRONMENT === 'INTEGRATED') {
config.optimization.minimize = false;
}
if (!options.isServer) {
console.log("Not Server");
config.output.publicPath = 'auto';
config.plugins.push(
new NextFederationPlugin({
name: 'cirrus',
filename: 'static/chunks/remoteEntry.js',
exposes: {
'./FederatedRouter': './lib/FederatedRouter',
...exposedModules
},
remoteType: 'var',
remotes: {},
shared: {
'#transcriptic/amino': {
requiredVersion: deps['#transcriptic/amino'],
singleton: true
},
react: {
requiredVersion: deps.react,
singleton: true
},
'react-dom': {
requiredVersion: deps['react-dom'],
singleton: true
},
'#strateos/micro-apps-utils': {
requiredVersion: deps['#strateos/micro-apps-utils'],
singleton: true
}
},
extraOptions: {
// We need to override the default module sharing behavior of this plugin as that assumes a nextjs host
// and thus next modules will be provided by the parent application.
// However, web is currently NOT a nextjs application so this child application so that assumption is
// invalid. Note that this means we need to ensure we explicitly specify common modules such as `react`
// in the `shared` key above.
skipSharingNextInternals: true
}
})
);
} else {
console.log("Is Server");
}
return config;
},
// Note: Annoyingly, NextJS automatically automatically appends a `_next` directory for assetPrefix
// but NOT public path so we'll have to manually include it here.
publicPath: `${assetBasePath}/_next/`,
// Note: If serving assets via CDN, assetPrefix is required to help resolve static assets.
// Also, NextJS automatically appends and expects a `_next` directory to the assetPrefix path.
// See https://nextjs.org/docs/api-reference/next.config.js/cdn-support-with-asset-prefix
assetPrefix: process.env.CDN_PATH ? assetBasePath : undefined,
distDir: 'dist',
// Use index react-router as fallback for resolving any pages that are not directly specified
async rewrites() {
return {
fallback: [
{
source: '/:path*',
destination: '/'
}
]
};
}
};
Tried upgrade NextJS from 12.1.6 to 12.2.2
Tried upgrade Webpack from 5.74.0 to 5.75.0
Cleaned cache by sh 'yarn cache clean'
Tried clear env by sh 'env -i PATH=$PATH make build-snapshot'
Hash of node modules by tar -cf - node_modules | md5sum
Downgraded "#module-federation/nextjs-mf" from 5.12.9 to 5.10.5
Verified file writing permission
I want to let authorized end users (non-admin) upload images to the media library straight from frontend (with no view restrictions - just public after upload).
How can I let the user see only his own folder during upload? Can I create a folder with ID of the user? How to create a folder programmatically? Is it even possible? Or I gotta use Cloudinary or S3?
To extend functionality of Strapi plugin we need to copy the needed plugin from node_modules to src/extensions
In this particular example, we just want to modify `add() a little
src\extensions\upload\services\upload.js
async add(values, { user } = {}) {
const fileValues = { ...values };
if (user) {
fileValues[UPDATED_BY_ATTRIBUTE] = user.id;
fileValues[CREATED_BY_ATTRIBUTE] = user.id;
fileValues.username = user.username; // <<< Here
}
sendMediaMetrics(fileValues);
const res = await strapi.query(FILE_MODEL_UID).create({ data: fileValues });
await this.emitEvent(MEDIA_CREATE, res);
return res;
},
Maybe it won't be enough, I just don't remember all the steps, but I think this would do the trick. If not - just go through the chain of invocations and make sure user.username at this stage is not undefined and it'll work.
I want the user to be able download images from the project /assets folder into an external publically accessible folder. This works fine on Android but in iOS everything saves under the app's unique ID directory so not sure if this possible. Essentially I want the user to save a photo from inside the app and then be able to open it easily from within their phone's default Photos app.
With the final photoPath var in the code below I can display the image in a <img tag using the DomSanitizer. Though still not sure how to make the file accessible on the user's system from outside the app.
this.http.get('/assets/img/' + item.img, { responseType: 'blob' })
.subscribe(res => {
const reader = new FileReader();
reader.onloadend = () => {
var base64data = reader.result;
await Filesystem.appendFile({
data: base64data.toString(),
path: item.file,
directory: FilesystemDirectory.Documents
})
const finalPhotoUri = await Filesystem.getUri({
directory: FilesystemDirectory.Documents,
path: item.file
});
let photoPath = Capacitor.convertFileSrc(finalPhotoUri.uri);
console.log("Photo Path: ", photoPath);
}
reader.readAsDataURL(res);
console.log(res);
});
I am using this Ghost plugin to store image data on Google Drive. Recently, images have stopped loading with this error page downloaded in place of the image:
The site is running in a containerized Ghost instance on Google Cloud Run, source here
Do I need to open a support ticket somewhere to resolve this? Site in question is here
EDIT: Here is the code used to access the saved content.
jwtClient.authorize(function(err, tokens) {
if (err) {
return next(err);
}
const drive = google.drive({
version: API_VERSION,
auth: jwtClient
});
drive.files.get(
{
fileId: id
},
function(err, response) {
if (!err) {
const file = response.data;
const newReq = https
.request(
file.downloadUrl + "&access_token=" + tokens.access_token,
function(newRes) {
// Modify google headers here to cache!
const headers = newRes.headers;
headers["content-disposition"] =
"attachment; filename=" + file.originalFilename;
headers["cache-control"] = "public, max-age=1209600";
delete headers["expires"];
res.writeHead(newRes.statusCode, headers);
// pipe the file
newRes.pipe(res);
}
)
.on("error", function(err) {
console.log(err);
res.statusCode = 500;
res.end();
});
req.pipe(newReq);
} else {
next(err);
}
}
);
});
Your problem is related to file.downloadUrl. This field is not guaranteed to work and is not supposed to be used to download files.
The correct way to do this is to use the webContentLink property instead. You can look at here for reference.
I am using AWS S3 and Cloudfront to store images and render images on a webpage. I have managed to generate a signed URL using Cloudfront to render S3 Objects.
The problem is, I tried attaching that signed URL within an a tag for href='' tagger. I was hoping that, when users clicked on the link, it would direct them to the image through the web. Instead, the file is downloaded when a user clicks on the link.
How photo key is generated
var photoKey = $ctrl.clientDetail["_id"] + '/' + fileName;
I created a subdirectory for each user, and store the file in the respective subdirectory.
My Upload Function in AngularJS
s3.upload({
Key: photoKey,
Body: file,
ACL: 'public-read',
Metadata: {
'id': $ctrl.clientDetail["_id"],
'phone': $ctrl.phone
}
}, function(err, data){
if (err) {
$scope.uploadedFail = true;
console.log(err)
genericServices.setErrorInfo($scope.configErrorAddDocAlert, addDoc_reason);
}
else {
$scope.uploadedSuccess = true;
genericServices.setSuccessInfo($scope.configSuccessAddDocAlert, addDoc_success);
}
}
);
What I found interesting was that when I manually uploaded a file in a subdirectory in the AWS S3 console, the generated URL was able to serve the file through the web.
How do I make it, so that the Object within the bucket is not a downloadable file, but a link?
For those who faced the same problem, here is the solution.
When uploading, specify the content-type.
S3 Upload Process modified
s3.upload({
Key: photoKey,
Body: file,
ContentType: file.type,
Metadata: {
'id': $ctrl.clientDetail["_id"],
'phone': $ctrl.phone
}
}, function(err, data){
if (err) {
$scope.uploadedFail = true;
console.log(err)
genericServices.setErrorInfo($scope.configErrorAddDocAlert, addDoc_reason);
}
else {
$scope.uploadedSuccess = true;
genericServices.setSuccessInfo($scope.configSuccessAddDocAlert, addDoc_success);
}
}
);
Initially, the type was application/octet-stream, so it was only downloadable.
Thanks you #michael-sqlbot for the comment