What is a proper way to upload an external image via URL into strapi on backend-side?
I had tried to load image with node-fetch and processed it with buffer()/blob()/blob().stream() and then passed it into strapi.plugins['upload'].services.upload.upload(). Also tried to generate FormData in node.js and passed it into upload service but still didn't help.
How to convert image buffer from fetch into suitable type for upload service?
I used axios and it was on client, but you can try in on server too I think.
This worked for me:
Fetch an image and create File instance from it
async getImage(imageUrl, imageName) {
const response = await axios.get(imageUrl, { responseType: 'blob' });
const mimeType = response.headers['content-type'];
const imageFile = new File([response.data], imageName, { type: mimeType });
return imageFile;
}
GraphQL API query
{
query: `
mutation($files: [Upload!]!) {
multipleUpload(files: $files) {
id
}
}
`,
variables: {
files: [
// your files to upload
]
}
}
Then I called this mutation and it worked perfect.
Resources that I used to find this solution:
https://www.freecodecamp.org/news/how-to-manage-file-uploads-in-graphql-mutations-using-apollo-graphene-b48ed6a6498c/
Client side convert png file stream into file object
https://github.com/jaydenseric/graphql-multipart-request-spec
Related
I have Angular running on the FrontEnd and Firebase Admin SDK for Node.js on the BackEnd.
What I want to achieve is to allow the user to select an image from his computer, using a simple <input> of type file. When I receive the user image which is of type File on the Angular side, I want to send this to my Node.js server and let him upload it to the Firebase Storage.
Here's how I'm sending the image to Node.js:
method(imageInput): void {
const image: File = imageInput.files[0];
const reader = new FileReader();
reader.addEventListener('load', (event: any) => {
const imageData = {
source: event.target.result,
file: image
}
this.myService.uploadImage(imageData.file).subscribe(
(res) => {
// image sent successfully
},
(err) => {
// error
})
});
reader.readAsDataURL(image);
}
So on the Node.js side I don't see a way to upload this image.
I'm trying:
admin.storage().bucket().upload(imageFromAngular, { --> Here's the problem
destination: "someDestination/",
contentType: "image/png",
metadata: {
contentType: "image/png"
}
}).then(() => {
// send successful response
}).catch(err => {
// send error response
});
The issue comes from the fact that the upload method only takes as a parameter the path to the image and the options. However in this case I can't pass the path to the image, rather I can pass the image itself. I read this - https://googleapis.dev/nodejs/storage/latest/ but I couldn't find anything that would suit my needs.
What would be the correct way to do this ?
Update:
Here's a more detailed explanation to the approach I took:
I'm using the arrayBuffer method of the image File inside my method. This method returns a promise of type ArrayBuffer. I get the value and send it to my Server.
The Server uses Buffer.from(ArrayBuffer, 'base64') to convert the data and then I can safely use the save API (https://googleapis.dev/nodejs/storage/latest/File.html#save).
To get the image later on I use download - (https://googleapis.dev/nodejs/storage/latest/File.html#download).
You can write a byte stream (or a Buffer) to Cloud Storage.
createWriteStream() API for streaming data to Cloud Storage: https://googleapis.dev/nodejs/storage/latest/File.html#createWriteStream
save() API for writing buffered data to Cloud Storage: https://googleapis.dev/nodejs/storage/latest/File.html#save
I try to upload a photo that I have in a URL on another server, but it does not work for me or I do not know how to upload them in this case I am going to upload a photo but I also want to upload files that will upload to that URL.
const img = await fetch("http://example.com/api/photo")
await gapi.client.drive.files.create({
resource: {
name: "New Folder",
body: img,
}
})
The simple anwser is you cant do it like that. The file being Uploaded must be sent in the form of a stream
Download the file to your own machine and then upload it from there. Or try to figure out how to turn your url into a stream.
var fileMetadata = {
'name': 'photo.jpg'
};
var media = {
mimeType: 'image/jpeg',
body: fs.createReadStream('files/photo.jpg')
};
drive.files.create({
resource: fileMetadata,
media: media,
fields: 'id'
}, function (err, file) {
if (err) {
// Handle error
console.error(err);
} else {
console.log('File Id: ', file.id);
}
});
I believe your goal as follows.
You want to download an image data from an URL, and want to upload the downloaded image data to Google Drive.
From your script, the image data is downloaded by const img = await fetch("http://example.com/api/photo").
You want to achieve this using googleapis for Javascript.
Modification points:
In this case, it retrieves Blob of image data from fetch, and the blob is uploaded to Google Drive.
Unfortunately, in the current stage, it seems that although googleapis for Javascript can create new file with the metadata, the file content cannot be included. By this, in this answer, I use the method of this thread. The downloaded image data is uploaded using fetch with multipart/form-data.
When above poiints are reflected to your script, it becomes as follows.
Modified script:
const img = await fetch("http://example.com/api/photo").then((e) => e.blob());
const fileMetadata = {name: "sampleName"}; // Please set filename.
const form = new FormData();
form.append('metadata', new Blob([JSON.stringify(fileMetadata)], {type: 'application/json'}));
form.append('file', img);
fetch('https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart', {
method: 'POST',
headers: new Headers({'Authorization': 'Bearer ' + gapi.auth.getToken().access_token}),
body: form
}).then(res => res.json()).then(res => console.log(res));
By this modification, the downloaded image data is uploaded to Google Drive with multipart/form-data.
Note:
In this modification, it supposes as follows.
Your URL of http://example.com/api/photo is the direct link of the image data.
Your authorization script can be used for uploading a file to Google Drive.
In this answer, as a sample script, the file is uploaded with uploadType=multipart. In this case, the maximum file size is 5 MB. Please be careful this. When you want to upload the file with the large size, please check the resumable upload. Ref
References:
Google API Client Library for JavaScript
Using Fetch
Files: create
Upload file data
Related question
How I can upload file to google drive with google drive api?
I am using the strapi upload plugin with s3 as a provider. It is working great when hitting the upload api endpoint on my strapi instance (/upload). However, I have a cron job in our repo that checks images in an s3 bucket and uploads them. Is there any way to call the upload plugin using the global strapi object, without having to make an http request in the cron job? The latter seems a little strange, since the cron job is running on the same server as strapi.
In my config/functions/cron.js file, I currently have this:
const imageBuffer = await fetch(imageURL).then((response) => response.buffer());
const formData = new FormData();
formData.append('files', imageBuffer, { filename: imageURL.split('/').pop() });
const uploadResult = await fetch('xxxxx/upload', {
method: 'POST',
body: formData
}).then((response) => response.json());
I would prefer to do something simple like:
const imageBuffer = await fetch(imageURL).then((response) => response.buffer());
await strapi.plugins.upload(imageBuffer)
I have been trying to reverse engineer the what the plugin does in its controller file, but that doesn't seem ideal either.
Any help from Strapi experts would be appreciated!
I found the solution and it's:
return strapi.plugins['upload'].services.upload.upload(
{ data: {fileInfo: {}}, files: {
path: path.resolve("public/uploads" + filename), // Put your file path
name: "asd.png",
type: 'image/png'
}}
)
I would upload an audio file to azure blob storage.
First I make a http request to url to get the file .
Then I would "directly" save it to azure blob storage without the need to store it in server then upload it.
Here's my code:
request
.get(url, {
auth: {
bearer: token
}
})
.on("response", async function(response) {
const res = await blobService.createAppendBlobFromStream(
"recordings", // container name
"record.wav",
response, // should be stream
177777, // stream length
function(err, res) {
try {
console.log(res);
} catch (err) {
console.log(err);
}
}
);
});
Actually when I upload a file in blob storage and check my database I get an empty file with no data inside, I think I'm not sending the data stream correcty.
What I expect is to get the audio file in blob storage with data inside that I get from the get request
I should also specify the stream length but I don't know how to get it I putted a random number but it should be the right stream length. I have checked response object but I havn't found that infomation.
I think you can't upload a file directly so first of all create a folder in your blob storage then create a file.
Read the selected file data and write it into the created file using file stream or else.
here is the upload file code,
app.post('/upload',function(req,res){
if(req.files.fileInput){
var file = req.files.fileInput,
name = file.name,
type = file.mimetype;
var uploadpath = __dirname + '/uploads/' + name;
file.mv(uploadpath,function(err){
if(err){res.send("Error Occured!")}
else {res.send('Done! Uploading files')}
});
}
else {
res.send("No File selected !");
res.end();
};
})
I'm building a webapp that needs to display some images, the frontend is build with VueJS and the backend is build with AdonisJS.
I'm currently having a problem that I'm uploading images from my frontend to my backend. AdonisJS generates a storage path that is local. As example, I upload from my frontend in this form:
Input form
That uses this code on the VueJS side:
let formData = new FormData();
let imagefile = document.querySelector('#file');
formData.append("image", imagefile.files[0]);
axios.post('/users/' + this.user.id + "/image", formData, {
headers: {
'Content-Type': 'image/*'
}
})
And on the AdonisJS side:
* updateProfilePicture(request, response) {
const image = request.file('image', {
maxSize: '20mb',
allowedExtensions: ['jpg', 'png', 'jpeg']
})
const userId = request.param('id');
const user = yield User.findOrFail(userId)
const fileName = `${new Date().getTime()}.${image.extension()}`
yield image.move(Helpers.storagePath(), fileName)
if (!image.moved()) {
response.badRequest(image.errors())
return
}
user.profilepicture = image.uploadPath()
yield user.save();
response.ok(user);
}
Which is working at the moment, but that generates a path that is used by AdonisJS:
ProjectFolder/backend/storage/1500586654324.jpg
VueJS is located in:
ProjectFolder/frontend/*
How can I use my uploaded images in the frontend? Is there some way that these frameworks can be coupled?
There's multiple way to get this image accessible via the browser.
You create a route within Adonis to handle a "media" route (like ~/media/1500586654324.jpg). This route will take the image ID and send the correspondant image inside your storage folder.
You don't upload your image to your storage folder and instead put them directly into the public folder of your application that means you can access the image directly via its URL.
I prefer using the first option since my public directory is 100% generated via script.