Hi, I’m building a folders-files service with cut©&paste actions, and I need to create a custom controller to clone or duplicate media files in server. Keeping in mind that upload service can apply to any provider.
Any idea of how to begin?
System Information
Finally I adopted this solution:
/src/api/file/routes/01-custom.js:
module.exports = {
routes: [
{
method: 'POST',
path: '/files/clone/:id',
handler: 'file.clone',
}
]
}
/src/api/file/controllers/file.js:
'use strict';
/**
* file controller
*/
const os = require('os');
const path = require('path');
const fs = require('fs');
const fse = require('fs-extra');
const http = require('http');
const https = require('https');
const FormData = require('form-data');
const fetch = require('node-fetch');
// 'name' is optional file name
async function urlDownload(url, name) {
const tmpWorkingDirectory = await fse.mkdtemp(path.join(os.tmpdir(), 'strapi-upload-'));
const file = fs.createWriteStream(tmpWorkingDirectory + "/" + (name || path.basename(url)))
return new Promise((resolve, reject) => {
const protocol = url.match('https://') ? https : http
let req = protocol.get(url, function (response) {
response.pipe(file);
// after download completed close filestream
file.on("finish", () => {
file.close();
resolve(file.path)
});
})
req.on('error', err => {
reject(err);
});
});
}
async function cloneMedia(strapi, ctx, url, name) {
const {
host,
port
} = strapi.config.get('server');
const urlget = url.match(/^https?:\/\//) ? url : `http://${host}:${port}` + url
const filePath = await urlDownload(urlget, name)
const headers = {}
const fields = ['authorization', 'Authorization']
for (const key of fields)
if (ctx.request.headers[key])
headers[key] = ctx.request.headers[key]
if (filePath) {
const form = new FormData()
form.append("files", fs.createReadStream(filePath))
return await fetch(`http://${host}:${port}/api/upload`, {
headers,
method: 'POST',
body: form,
})
.then(response => response.json())
}
return null
}
const {
createCoreController
} = require('@strapi/strapi').factories;
module.exports = createCoreController('api::file.file',
({
strapi
}) => ({
// clone file and related media
async clone(ctx) {
const {
id,
} = ctx.params
let file = await strapi.service("api::file.file").findOne(id, {
populate: ['media']
})
if (!file)
return ctx.notFound(`file ${id} not found`)
const response = await cloneMedia(strapi, ctx, file.media.url, file.media.name)
.catch(error => {
return ctx.internalServerError(error.message)
})
if (response && !response.error) {
delete file.id
file.media = response[0].id
const result = await strapi.service("api::file.file").create({
data: file
})
return {
data: result, meta:{}
}
}
return {
data: null,
error: "Error in server"
}
}
}))
Hi @msoler75 . I needed something similar to what you described and tried to implement your solution. But I keep getting this error:
{
data: null,
error: {
status: 403,
name: 'ForbiddenError',
message: 'Forbidden',
details: {}
}
}
Have any idea?