fine uploader S3 with large file fails - fine-uploader

I am trying to upload a file upto 500MB using fine uploader s3.
Version is 5.0.9.
This is the code which I used.
function getFineUploaderOptions() {
var options = {
debug: true,
template: "qq-template-manual-noedit",
autoUpload: false,
multiple: false,
request: {
endpoint: UploadVars.endpoint,
accessKey: UploadVars.accessKey
},
signature: {
endpoint: "entry_essay_handler_s3.php"
},
uploadSuccess: {
endpoint: "entry_essay_handler_s3.php?success"
},
// required if non-File-API browsers, such as IE9 and older, are used
iframeSupport: {
localBlankPagePath: 'blank.html'
},
validation: {
allowedExtensions: UploadVars.allowedExtensions,
sizeLimit: UploadVars.sizeLimit
},
showMessage: function(message) {
// Using Twitter Bootstrap's classes and jQuery selector and method
console.log(message);
$('#imageUploadmsg').html(message);
$('#imageUploadmsg').css('visibility','visible');
},
objectProperties: {
key: function (id) {
return getUploadPath(id);
}
},
chunking: {
enabled: true
},
retry: {
enableAuto: true
},
resume: {
enabled: true
},
messages:{
unsupportedBrowser: "You need to update your browser in order to upload a file."
}
};
return options;
}
And this is uploadVars variable.
var UploadVars = {
accessKey: 'somevalue',
endpoint: "http://s3.amazonaws.com/somevalue",
allowedExtensions: ['zip', 'rar'],
izeLimit: '524288000',
};
And on the upload button I used this code.
$('#imageUpload').fineUploaderS3(getFineUploaderOptions())
.on('upload', function(event, id, name) {
})
.on('submit', function(event, id, name) {
})
.on('complete', function(event, id, name, json, xhr) {
})
.on('cancel', function(event, id, name, json, xhr) {
})
.on('autoRetry', function (event, id, name, attemptNumber) {
// leave the code for auto Retry
})
.on('error', function(event, id, name, errorReason, xhr) {
});
I always get these errors.
[Fine Uploader 5.0.9] Error attempting to parse signature response:
SyntaxError: Unexpected token
custom.fineuploader-5.0.9.js:212 [Fine Uploader 5.0.9] Received an empty or invalid response from the server!
I confirm it is working very well with small size files.
And this is S3 CORS settings.
<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<CORSRule>
<AllowedOrigin>*</AllowedOrigin>
<AllowedMethod>POST</AllowedMethod>
<AllowedMethod>PUT</AllowedMethod>
<AllowedMethod>DELETE</AllowedMethod>
<MaxAgeSeconds>3000</MaxAgeSeconds>
<ExposeHeader>ETag</ExposeHeader>
<AllowedHeader>content-type</AllowedHeader>
<AllowedHeader>origin</AllowedHeader>
<AllowedHeader>x-amz-acl</AllowedHeader>
<AllowedHeader>x-amz-meta-qqfilename</AllowedHeader>
<AllowedHeader>x-amz-date</AllowedHeader>
<AllowedHeader>authorization</AllowedHeader>
</CORSRule>
</CORSConfiguration>
And I also updated php.ini file upload_max_filesize = 500MB, post_max_size = 600M
and phpinfo confirms I specified correct sizes.
And this is the signing process in php side.
function signRequest() {
header('Content-Type: application/json');
$responseBody = file_get_contents('php://input');
$contentAsObject = json_decode($responseBody, true);
$jsonContent = json_encode($contentAsObject);
$headersStr = null;
if (isset($contentAsObject["headers"]))
$headersStr = $contentAsObject["headers"];
if ($headersStr) {
return signRestRequest($headersStr);
}
else {
return signPolicy($jsonContent);
}
}
function signRestRequest($headersStr) {
if (isValidRestRequest($headersStr)) {
$response = array('signature' => sign($headersStr));
}
else {
logMessage("Signin is failed. Your file could not be uploaded at this time. please try again later");
echo json_encode(array("invalid" => true, "success" => false));
return false;
}
return true;
}
function isValidRestRequest($headersStr) {
global $expectedBucketName;
$pattern = "/\/$expectedBucketName\/.+$/";
preg_match($pattern, $headersStr, $matches);
return count($matches) > 0;
}
function signPolicy($policyStr) {
$policyObj = json_decode($policyStr, true);
if (isPolicyValid($policyObj)) {
$encodedPolicy = base64_encode($policyStr);
$response = array('policy' => $encodedPolicy, 'signature' => sign($encodedPolicy));
echo json_encode($response);
}
else {
logMessage("Your file could not be uploaded at this time. please try again later");
echo json_encode(array("invalid" => true, "success" => false));
return false;
}
return true;
}
function isPolicyValid($policy) {
global $expectedMaxSize, $expectedBucketName;
$conditions = $policy["conditions"];
$bucket = null;
$parsedMaxSize = null;
for ($i = 0; $i < count($conditions); ++$i) {
$condition = $conditions[$i];
if (isset($condition["bucket"])) {
$bucket = $condition["bucket"];
}
else if (isset($condition[0]) && $condition[0] == "content-length-range") {
$parsedMaxSize = $condition[2];
}
}
return $bucket == $expectedBucketName && $parsedMaxSize == (string)$expectedMaxSize;
}
function sign($stringToSign) {
global $clientPrivateKey;
return base64_encode(hash_hmac(
'sha1',
$stringToSign,
$clientPrivateKey,
true
));
}
Can anybody help me out this issue?
Thanks

Your signature server is returning an invalid response to Fine Uploader's signature request. Fine Uploader expects a valid JSON response, but your server is returning HTML followed by a JSON string. For example, before the signature JSON string, your server is including this in the signature response:
<br />
<b>Deprecated</b>: mysql_connect(): The mysql extension is deprecated and will be removed in the future: use mysqli or PDO instead in <b>/Applications/MAMP/htdocs/InSite_dev/wwwroot/1620/common/config/config.php</b> on line <b>18</b><br />
<br />
<b>Warning</b>: Cannot modify header information - headers already sent by (output started at /Applications/MAMP/htdocs/InSite_dev/wwwroot/1620/common/config/config.php:18) in <b>/Applications/MAMP/htdocs/InSite_dev/wwwroot/1620/cookiehandler.php</b> on line <b>16</b><br />
<br />
<b>Warning</b>: Cannot modify header information - headers already sent by (output started at /Applications/MAMP/htdocs/InSite_dev/wwwroot/1620/common/config/config.php:18) in <b>/Applications/MAMP/htdocs/InSite_dev/wwwroot/1620/cookiehandler.php</b> on line <b>32</b><br />
<br />
<b>Warning</b>: session_start(): Cannot send session cache limiter - headers already sent (output started at /Applications/MAMP/htdocs/InSite_dev/wwwroot/1620/common/config/config.php:18) in <b>/Applications/MAMP/htdocs/InSite_dev/wwwroot/1620/entry_essay_handler_s3.php</b> on line <b>20</b><br />
<br />
<b>Warning</b>: Cannot modify header information - headers already sent by (output started at /Applications/MAMP/htdocs/InSite_dev/wwwroot/1620/common/config/config.php:18) in <b>/Applications/MAMP/htdocs/InSite_dev/wwwroot/1620/entry_essay_handler_s3.php</b> on line <b>370</b><br />

Related

uploading profile pic in hapijs 17.0

I am using hapijs version 17.0.1. I am trying to upload an image using ajax request on a hapijs route. Here is my AJAX code to upload profile pic:
var image_file_input = document.getElementById("user_profile_upload");
image_file_input.onchange = function () {
if(this.files != undefined)
{
if(this.files[0] != undefined)
{
var formData = tests.formdata ? new FormData() : null;
if (tests.formdata)
{
//alert(file)
formData.append('image_file', this.files[0]);
formData.append('userId', user_id);
formData.append('memberId', member_id);
}
$.ajax({
url: "/v1/User/uploadUserPic",
data: formData,
type: "POST",
dataType: "json",
contentType: false,
processData: false,
contentType: "multipart/form-data",
success: function(data){
console.log(data);
var errMsg = null;
var resData = null;
if(data.statusCode == 200)
{
resData = data.result;
}
else
{
alert(data.message)
}
},
error: function(error){
alert(error);
}
});
}
}
}
And here is my Hapijs route Code:
var uploadUserPic = {
method: 'POST',
path: '/v1/Module/uploadUserPic',
config: {
description: 'Update Image For User',
tags: ['api', 'User'],
auth: 'session',
payload: {
output: 'stream',
parse: true,
allow: 'multipart/form-data'
},
validate: {
payload: {
userId : Joi.string().regex(/^[a-f\d]{24}$/i).required(),
memberId: Joi.string().required(),
image_file: Joi.object().required(),
},
failAction: FailCallBack
}
},
handler: function (request, reply) {
var resultData = null;
var error = null;
return new Promise(function (resolve) {
var multiparty = require('multiparty');
var fs = require('fs');
var form = new multiparty.Form();
form.parse(request.payload, function (err, fields, files) {
if(err)
{
error = err;
resolve();
}
else
{
var mkdirp = require('mkdirp');
var img_dir = "./files/users/";
mkdirp(img_dir, function (err) {
if (err)
{
error = err;
console.error(err);
resolve();
}
else
{
var oldpath = files.image_file.path;
var newpath = "./files/users/"+requestPayload.userId+".png";
fs.rename(oldpath, newpath, function (err) {
if(err)
{
error = err;
}
resolve();
});
}
});
}
});
}).then(function (err, result) {
if(err) return sendError(err);
if(error) return sendError(error)
return {
"statusCode": 200,
"success": true
};
});
}
}
The above code gives me following error cannot read property 'content-length' of undefined on line form.parse(request.payload, function (err, fields, files) {});
Please let me know If I am doing something wrong. If I replace the url in ajax request with anohter url that I have written in php then it works perfectly. which means that something is wrong with my hapijs/nodejs code.
There's a good post on how to handle file uploads in Hapi.js (written in version 16) https://scotch.io/bar-talk/handling-file-uploads-with-hapi-js
Since you are using payload.parse = true, I am not seeing a particular reason why you have to use multiparty. I have the following working code that would save files (of any type) uploaded from client into uploads directory on the server (Please do not use directly on production as no sanitation is done)
{
path: '/upload',
method: 'POST',
config: {
payload: {
output: 'stream',
parse: true,
allow: 'multipart/form-data'
},
validate: {
payload: {
files: Joi.array().single()
}
}
},
handler: function(request) {
const p = request.payload, files = p.files
if(files) {
console.log(`${files.length} files`)
files.forEach(async file => {
const filename= file.hapi.filename
console.log(`Saving ${filename} to ./uploads`)
const out = fs.createWriteStream(`./uploads/${filename}`)
await file.pipe(out)
})
}
return {result: 'ok'}
}
}
You can use the following curl command to test
curl http://localhost:8080/upload -F 'files=#/path/to/a/note.txt' -F 'files=#/path/to/test.png' -vvv
There are a few issues with your code. First in your $.ajax call, you have specified contentType twice, although it's not a syntax error but it's careless to code like that. Second the function's signature inside your .then() block is incorrect. You are mixing the idea of Promise and callback. I don't think the following line will be triggered
if(err) return sendError(err);
One last trivial thing, you said you are using Hapi 17 but based on the handler function's signature
handler: function (request, reply) {
...
Seems you are not totally onboard with Hapi17 as the new signature is
handler: function (request, h) {
And it's not just the rename of reply to h.

How to send an ajax contact form with to a recipient email address

I've currently got this:
$.ajax({
url: '/some/url',
dataType: 'json',
type: 'POST',
data: formData,
success: function(data) {
if (window.confirm('Thank you for your message. Can I erase the form?')) {
document.querySelector('.form-input').val('');
}
},
error: function(xhr, status, err) {
console.error(status, err.toString());
alert('There was some problem with sending your message.');
}
});
Instead of it going to a URL, how can I change it to send directly to a specific email address? I am using this contact form with a React app I've created.
So react component, class based.
class Foo extends Component {
popupQuestion() {
// implement method
}
sendEmail() = () => {
axios.post('/some/url', {
subject: 'mail',
to: 'someone#example.com',
body: 'something',
name: 'name'
})
.then(function (response) {
popupQuestion();
})
.catch(function (error) {
console.log(error);
return 'Error occurred. Please refresh page and try again.';
});
}
render() {
return(
<form onSubmit={this.sendEmail}>
// ...
</form>
);
}
}
And php method that will be executed on some/url
public function sendEmailAction(): bool
{
$request = // get request;
$subject = $request->get('subject');
$to = $request->get('to');
$body = $request->get('body');
$name = $request->get('name');
$transport = (new Swift_SmtpTransport('smtp.example.org', 25))
->setUsername('your username')
->setPassword('your password');
$mailer = new Swift_Mailer($transport);
$message = (new Swift_Message($subject))
->setFrom(['mymail#exmaple.com' => 'me'])
->setTo([$to => $name])
->setBody($body);
$sent = $mailer->send($message);
return $sent ? true : false;
}

MongoDB/Multer Query DB then upload File

I want to query a database and if result is found upload a file. At the moment it's the other way around. First the image is uploaded then the db is searched and updated.
A value I need, cid(req.body.cid), can only be accessed after upload() is called so that kinda complicates things, this is my current, working, code :
var multer = require('multer');
var upload = multer({
dest: './public/images/usercontent',
limits: {
files: 1,
fields: 1,
fileSize: 10000
},
fileFilter: function fileFilter(req, file, cb) {
if (file.mimetype !== 'image/png' && file.mimetype !== 'image/jpg' && file.mimetype !== 'image/jpeg') {
req.multerImageValidation = 'wrong type';
return cb(null, false);
}
return cb(null, true);
}
}).single('uploads[]');
router.post('/uploadimg', isLoggedIn, function(req, res, next) {
upload(req, res, function(err) {
if (err) {
if (err.code === 'LIMIT_FILE_SIZE') {
return res.send({
statusText: 'fileSize'
});
}
return res.send({
statusText: 'failed'
});
}
if (req.multerImageValidation) {
return res.send({
statusText: 'wrongType'
});
}
Company.findOneAndUpdate({
ownedBy: req.user.local.username,
_id: req.body.cid // value that is sent with the image
}, {
icon: 'images/usercontent/' + req.file.filename
}, function(err, result) {
if (err) {
return res.send({
statusText: 'failed'
});
}
if (!result) {
return res.send({
statusText: 'failed'
});
}
res.send({
statusText: 'success'
});
});
//
});
});
Why don't you use the success callback for the db query to upload image and then use the success callback of the upload image function to update the db record with cid? At a very high level, this would be:
query db for record
//if record found, in success callback
attempt to upload image
//if image uploaded successfully and cid generated, in success callback
update db record
You can use the mongo update operation to ensure atomic operation

Winjs get request failing to return data

I encountered a strange problem. In my app I have the following code
WinJS.xhr({
url: 'http://bdzservice.apphb.com/api/Route?fromStation=София&toStation=Варна&date=30/08/2013&startTime=00:00&endTime=24:00'
}).then(function (success)
{
console.log(success);
},
function (error)
{
console.log(error);
}
);
The problem is I get an empty response text (with status 200). The Url I provided returns data through the browser and other rest clients, but in the app I get no data. Where might be the problem?
You need to encode query string parameters via encodeURIComponent (browser does this for you automatically when pasting url).
Following code will do the trick:
function serialize (obj) {
var str = [];
for (var p in obj) {
if (obj.hasOwnProperty(p)) {
str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p]));
}
}
return str.join("&");
};
var request = {
fromStation: 'София',
toStation: 'Варна',
date: '30/08/2013',
startTime: '00:00',
endTime: '24:00'
};
WinJS.xhr({
url: 'http://bdzservice.apphb.com/api/Route?' + serialize(request)
}).then(function(success) {
console.log(success);
},
function(error) {
console.log(error);
}
);

Uploading files to tastypie with Backbone?

Checked some other questions and I think my tastypie resource should look something like this:
class MultipartResource(object):
def deserialize(self, request, data, format=None):
if not format:
format = request.META.get('CONTENT_TYPE', 'application/json')
if format == 'application/x-www-form-urlencoded':
return request.POST
if format.startswith('multipart'):
data = request.POST.copy()
data.update(request.FILES)
return data
return super(MultipartResource, self).deserialize(request, data, format)
class ImageResource(MultipartResource, ModelResource):
image = fields.FileField(attribute="image")
Please tell me if that's wrong.
What I don't get, assuming the above is correct, is what to pass to the resource. Here is a file input:
<input id="file" type="file" />
If I have a backbone model img what do I set image to?
img.set("image", $("#file").val()); // tastypie doesn't store file, it stores a string
img.set("image", $("#file").files[0]); // get "{"error_message": "'dict' object has no attribute '_committed'" ...
What do I set my backbone "image" attribute to so that I can upload a file to tastypie via ajax?
You may override sync method to serialize with FormData api to be able to submit files as model's attributes.
Please note that it will work only in modern browsers. It worked with Backbone 0.9.2, I advise to check the default Backbone.sync and adopt the idea accordingly.
function getValue (object, prop, args) {
if (!(object && object[prop])) return null;
return _.isFunction(object[prop]) ?
object[prop].apply(object, args) :
object[prop];
}
var MultipartModel = Backbone.Model.extend({
sync: function (method, model, options) {
var data
, methodMap = {
'create': 'POST',
'update': 'PUT',
'delete': 'DELETE',
'read': 'GET'
}
, params = {
type: methodMap[method],
dataType: 'json',
url: getValue(model, 'url') || this.urlError()
};
if (method == 'create' || method == 'update') {
if (!!window.FormData) {
data = new FormData();
$.each(model.toJSON(), function (name, value) {
if ($.isArray(value)) {
if (value.length > 0) {
$.each(value, function(index, item_value) {
data.append(name, item_value);
})
}
} else {
data.append(name, value)
}
});
params.contentType = false;
params.processData = false;
} else {
data = model.toJSON();
params.contentType = "application/x-www-form-urlencoded";
params.processData = true;
}
params.data = data;
}
return $.ajax(_.extend(params, options));
},
urlError: function() {
throw new Error('A "url" property or function must be specified');
}
});
This is excerpt from upload view, I use <input type="file" name="file" multiple> for file uploads so user can select many files. I then listen to the change event and use collection.create to upload each file.
var MultipartCollection = Backbone.Collection.extend({model: MultipartModel});
var UploadView = Backbone.View.extend({
events: {
"change input[name=file]": "changeEvent"
},
changeEvent: function (e) {
this.uploadFiles(e.target.files);
// Empty file input value:
e.target.outerHTML = e.target.outerHTML;
},
uploadFiles: function (files) {
_.each(files, this.uploadFile, this);
return this;
},
uploadFile: function (file) {
this.collection.create({file: file});
return this;
}
})

Resources