I try uploading a text file from client desktop to a server. A web page is built on js like this:
const usunDane = document.getElementById('cbxUsunDane').checked;
const nadpiszDane = document.getElementById('cbxNadpiszDane').checked;
formData.append("file", selectedFile);
formData.append("usunDane", usunDane);
formData.append("nadpiszDane", nadpiszDane);
await fetch('php/produktyUpload.php', {
method: "POST",
body: formData
});
I added usunDane and nadpiszDane hoping to pass it into produktyUpload.php file, however I do not know how to properly read it. I tried this:
if ($_FILES['nadpiszDane']=="true") {
error_log("UPDATE ", 3, "/var/www/html/errors.log");
} else {
error_log("INSERT ", 3, "/var/www/html/errors.log");
}
... but this does not work.
When I look at the browser I can see that both variables are passed to php file...
*
Content-Disposition: form-data; name="usunDane"
true
Content-Disposition: form-data; name="nadpiszDane"
true
*
Could someone please advice on how to pass/read these variables?
Related
DISCLAIMER: Before creating this question, I've checked here, here and here, and also checked Laravel docs.
Context
Laravel 9 full-stack
No JS framework on front-end, which means I'm using vanilla JS
The folders on Storage are setted like this:
storage
app
public
folder1
folder1A
folder1B
folder1C
etc
The files stored in each folder1X are .pdf format and I don't know its names.
No folders are empty, nor with invalid/corrupted files.
The problem
I have a FileController.php to download files that are inside a folder1X/ directory. The method to download it is as follows:
public function downloadFileFromStorage(Request $request): mixed
{
$dirpath = $request->dirpath; // dirpath = public/folder1/folder1X.
$files = Storage::allFiles($dirpath);
return response()->download(storage_path('app\\' . $files[0]));
}
(Note: dirpath is sent in a axios request by client and is also fetched from database on a previous request)
My Javascript CLI needs to enable the download of this file. The download is enabled by clicking on a button. The button calls downloadPDF(dirpath) which works as follows:
function downloadPDF(dirpath) {
axios.post('/download-pdf-file', { dirpath })
.then(
success => {
const url = success.data
const a = document.createElement('a')
a.download = 'file.pdf'
a.href = url
a.click()
},
error => {
console.log(error.response)
}
)
}
But, when I run this function, I get a about:blank#blocked error.
Attempts
Changed the a HTML DOM approach to a window.open(url) on client;
Changed response() to Storage::download($files[0], 'file-name.pdf'), and with this I also tried using Blob on client as follows:
success => {
const blob = new Blob([success.data], { type: 'application/pdf' })
const fileURL = URL.createObjectURL(blob)
window.openURL(fileURL)
},
Also mixed Blob with the a HTML DOM approach;
Changed storage_path argument to /app/public/ before concatenating to $files[0].
UPDATE
Following tips from #BenGooding and #cengsemihsahin, I changed files to the following:
JS
// FileDownload is imported on a require() at the code beginning
function downloadPDF(dirpath) {
axios({
url: '/download-pdf-file',
method: 'GET',
responseType: 'blob',
options: {
body: { dirpath }
}
}).then(
success => {
FileDownload(success.data, 'nota-fiscal.pdf')
}
)
}
PHP:
public function downloadFileFromStorage(Request $request): mixed
{
$dirpath = $request->dirpath; // dirpath = public/folder1/folder1X.
$files = Storage::allFiles($dirpath);
return Storage::download($files[0], 'filename.pdf');
}
and now it downloads a corrupted PDF that can't be opened.
Finally found the issue, and it was here:
axios({
url: '/download-pdf-file',
method: 'GET',
responseType: 'blob',
options: { // here
body: { dirpath } // here
}
})
Laravel's Request arrow operator -> can't fetch a GET body sent through options (At least, not on $request->key fashion; see more about it here) thus making me download a corrupted file - it wasn't fetching any file on Laravel as it didn't get any path at all.
Here is the solution I came with:
As I want to get a file in a route that doesn't change except for the 1X at folder1X, I'm processing the path obtained and sending the 1X as a GET query param:
let folderNumber = dirpath.split('/')
folderNumber = folderNumber[folderNumber.length].replaceAll('/', '')
axios({
url: '/download-pdf-file?folder=',
method: 'GET',
responseType: 'blob'
})
This way I don't pass the whole path to back-end and it's possible to get folderNumber by using $request->query():
public function downloadFileFromStorage(Request $request): mixed
{
$folderNumber = $request->query('folderNumber');
$folderPath = '/public/folder1/folder' . $folderNumber . '/';
$files = Storage::allFiles($folderPath);
return Storage::download($files[0], 'file-name.pdf');
}
In a nutshell:
To download files, use GET requests;
To send arguments within GET requests, use query parameters and fetch them with $request->query('keyname') (or find out another way. Good luck!);
I'm trying to use the Axios POST method to upload a file to Pinata IPFS:
FRONT-END:
<body>
<input type="file" id="file-upload" ></input>
<script> let file = document.getElementById("file-upload").value;</script>
</body>
FILE GETS SENT TO BACKEND THROUGH SOCKET.IO:
pin = (pinataApiKey, pinataSecretApiKey, file) => {
url = `https://api.pinata.cloud/pinning/pinFileToIPFS`;
const data = new FormData();
data.append("file", fs.createReadStream(`${file}`));
return axios.post(url, data, {
maxBodyLength: "Infinity",
headers: {
"Content-Type": `multipart/form-data; boundary=${data._boundary}`,
pinata_api_key: pinataApiKey,
pinata_secret_api_key: pinataSecretApiKey,
},
});
};
pin() only works when I use a local file path (i.e. C:/Users/anon/Desktop/project/untitled.png). If I try to use file.value (whose path is "C:\fakepath\testImage.jpeg"), the code doesn't work. I need the actual path of the uploaded file.
What do you get when you log file??
Perhaps you need to put this:
let file = document.getElementById("file-upload").value;
inside your js file:
And probably the best thing is to do some validations before the file is uploaded.
I dont get if i work with request correctly, after upload all files is 1 KB and i cant open them. How to create correct file? If i save file as .doc i can see:
------WebKitFormBoundaryt3UjlK5SVq8hgppA
Content-Disposition: form-data; name="file"
[object FileList]
------WebKitFormBoundaryt3UjlK5SVq8hgppA--
So my functions to submit in js file:
async submitFiles() {
let formData = new FormData();
formData.append('file', this.file);
console.log(this.file)
axios.put(`/api/v1/myapp/upload/${this.file[0].name}`,
formData,
{
headers: {
'Content-Disposition': 'attachment',
'X-CSRFToken': await this.getCsrfToken(),
},
}
).then(function () {
console.log('SUCCESS!!');
})
.catch(function () {
console.log('FAILURE!!');
});
},
To handle change of file in form
fileChanged(file) {
this.file = file.target.files
},
And finally my view.py
class FileUploadView(APIView):
parser_classes = [FileUploadParser]
def put(self, request, filename, format=None):
file_obj = request.data['file']
handle_uploaded_file(file_obj)
return Response({'received data': request.data})
Where
def handle_uploaded_file(f):
with open('path/to/my/folder/' + str(f.name), 'wb+') as destination:
for chunk in f.chunks():
destination.write(chunk)
[object FileList]
Oh, you serialized the whole FileList.
Change to: formData.append('file', this.file[0]);
If this won't work you may need to read the file's content.
Edit: it should be enough, according to MDN:
The field's value. This can be a USVString or Blob (including subclasses such as File). If none of these are specified the value is converted to a string.
I'm trying to upload a video to the Linkedin API as per the marketing API documentation. I've done the following so far:
Registered for a multi part asset upload and received the response containing an array of unique multipart URLs to push the corresponding chunks to
Fetched the chunks from an Amazon S3 bucket where they live using the Range header
Successfully uploaded all those chunks via PUT to their corresponding URLs, and stored their ETag and HTTP Status code values.
Created the finalise POST request body as per the link above using the etags and status codes.
Unfortunately my request fails with:
'com.linkedin.vector.utils.logic.LogicLayerInvalidException: Invalid complete multipartUpload request ...(stringified payload)'
The only part of the request body I haven't added from the example given is the "metadata" field- there's nothing in the documentation to explain what this is or where it's generated from. I'm assuming that's what is missing. Can anyone point me in the right direction please?
Code for the request in question is as follows:
// Loop over chunked download URLs and upload segments of S3 file.
for(let i = 0, l = uploadDetails.partUploadRequests.length; i < l; i++) {
const item: PartUploadRequest = uploadDetails.partUploadRequests[i];
const partialParams: GetObjectRequest = { Bucket: video.dynamoData.mp4Bucket, Key: video.dynamoData.mp4Outputs[0], Range: `bytes=${item.byteRange.firstByte}-${item.byteRange.lastByte}` };
console.log(`Requesting bytes ${item.byteRange.firstByte}-${item.byteRange.lastByte}`);
const s3PartialObject = await s3Client.getObject(partialParams).promise();
const response = await axios.put(item.url, s3PartialObject.Body, {
headers: {
...item.headers
}
});
const { status, headers } = response;
responses.push({
headers: {
ETag: headers.etag
},
httpStatusCode: status
});
};
// Send all chunk responses off and confirm video upload
const finaliseVideoPayload: LinkedinFinaliseVideoPostRequest = {
completeMultipartUploadRequest: {
mediaArtifact: registerVideoRequest.value.mediaArtifact,
partUploadResponses: responses
}
};
console.log(`Fetched all parts, readying finalise request with ${finaliseVideoPayload.completeMultipartUploadRequest.partUploadResponses.length} parts.`);
const json = await axios.post('https://api.linkedin.com/v2/assets?action=completeMultiPartUpload', finaliseVideoPayload, {
headers: {
'X-RestLi-Protocol-Version': '2.0.0',
'Authorization': 'Bearer ' + channel.token,
'Host': 'api.linkedin.com'
}
});
Thanks
I guess this has been already solved. Just in case if it is not.
There is a field 'metadata' which comes in the registerApi for multipart upload. This comes in the field.
uploadMechanism["com.linkedin.digitalmedia.uploading.MultipartUpload"].metadata
Apparently, this is required by the completeMultipart api.
So data will be
completeMultipartUploadRequest: {
mediaArtifact: registerVideoRequest.value.mediaArtifact,
metadata: registerVideoRequest.value.uploadMechanism["com.linkedin.digitalmedia.uploading.MultipartUpload"].metadata,
partUploadResponses: responses
}
It took me a day to figure out that the field which has no value(empty string) is required. Maybe a bug.
Hopefully this solves the issue.
I am using typescript (angular 4) to post to the salesforce endpoint
https://yourInstance.salesforce.com/services/data/v41.0/sobjects/ContentVersion/
My http request looks like the following
Request Headers
Content-Type: multipart/form-data; boundary="1524931327799"
Authorization: Bearer <token>
Request Body
--1524931327799
Content-Disposition: form-data; name="entity_document";
Content-Type: application/json; charset=UTF-8
{
"PathOnClient" : "IMG_0400.jpg",
"Title": "IMG_0400.jpg"
}
--1524931327799
Content-Type: image/jpeg
Content-Disposition: form-data; name="VersionData"; filename="IMG_0400.jpg"
/9j/4AAQSkZJRgABAQAAAQABAAD/4QBsRXhpZgAASUkqAAgA <rest of base64 data>
--1524931327799--
After opening the image on the salesforce platform I get an error that the image may be damaged or use a file format that Preview doesn’t recognize. When I open the image using text edit there is the identical base64 data that is sent in the request. It seems the problem comes with salesforce not recognizing that the file is an image and does not decode the base64 data. All and any help is welcomed! Thank you.
I just tried it using developer workbench and request like this should work just fine. Try not to define 2 different content types in the request, rather put definition of file into the VersionData attribute of ContentVersion Object
{
"PathOnClient" : "IMG_0400.jpg",
"Title": "IMG_0400.jpg",
"VersionData" : "4AAQSkZJRgABAQAAAQABAAD/4QBsRXhpZgAASUkqAAgA"
}
I was never able to post to the /ContentVersion/ endpoint. After doing some research the simplest solution I found was to use jsforce https://jsforce.github.io/.
Solution using jsforce:
1. Import jsforce library in your index.html "https://cdnjs.cloudflare.com/ajax/libs/jsforce/1.7.0/jsforce.min.js"
2. Import jsforce at the top of your angular component
declare var jsforce:any;
Start a connection with jsforce
var conn = new jsforce.Connection({
loginUrl: "https://test.salesforce.com/",
clientId : "",
clientSecret : "",
redirectUri : ""
});
3. Login to SF and post to composite endpoint using requestPost
var username = "";
var password = "";
conn.login(username, password, function(err, userInfo) {
if (err) { return console.error(err); }
var path = '/services/data/v41.0';
return conn.requestPost( path + '/composite/', {
'allOrNone' : true,
'compositeRequest' : [
{
'method' : 'POST',
'url' : path + '/sobjects/ContentVersion/',
'referenceId' : 'newFile',
'body' : {
'Title' : fileToPost.name,
'PathOnClient' : fileToPost.name,
'VersionData' : base64FileData
}
}
]
})