I am trying to save attachments in ravenDb. I am getting a file not found error.
MVC View:
<input type="file" name="file" id="Ids2" style="float:right"/>
Over an ajax call, I am passing the value of the file name selected in the above control to the controller method - which in turns sends the file name to a custom method called "Upload"
public virtual string Upload(string fileName)
{
IDocumentSession session = GetCurrentDocumentSession();
var id = "upload/" + randomGen();
session.Advanced.DatabaseCommands.PutAttachment(id,null,
File.ReadAllBytes(fileName), optionalMetaData);
return id;
}
I am getting C:\ProgramFiles (x86)....does not have the file specified.
Lets say in the view - I browsed to C:/Doc1.txt and clicked on Add button that saves bunch of other fields on the view and also picks up the file name/path from the file upload control.
I get an error at session.advance.databasecommands... line
Could not find file 'C:\Program Files (x86)\Common Files\Microsoft Shared\DevServer\10.0\Doc1.txt'.
If I manually move the Doc1.txt file to the above location, ravenDB saves the attachment and I can see it from localhost:8080/static/upload/keyvalue
How can I make ravenDB take the file from the location the user selects and not from the what it looks like a default location of c:programfiles.....
EDIT:
function () {
var iFile = iContainer.find( '#Ids2' ).val();
var DataToSave = {
'Attachment' : iFile
};
var encodedData = $.toJSON(DataToSave);
$.ajax({
type: 'POST' ,
url: '/AttController/Attach' ,
data: encodedData,
contentType: 'application/json; charset=utf-8' ,
success: function (rc) {
if (rc.Success) {
// more javascript reroutes..business logic
}
else {
alert(rc.Message);
}
},
error: function (xhr, ajaxOptions, thrownError) {
alert( 'Error attaching \n' + xhr.response);
}
});
};
Depending on the browser The html file control does not store the full path to the file. If you use Chrome and debug the script
var iFile = iContainer.find( '#Ids2' ).val();
Will return something like C:\fakepath\yourfile.txt. where as with IE the full path is returned.
Also you in your Ajax you are not pushing the bytes of the file but only the filename which means unless you are going to only ever run this website in a browser on the webserver the chances of the file being in the same place as the webserver is slim.
If you are trying to upload a file via ajax to a MVC controller I would suggest uploadify.
$("#Ids2").uploadify(
{
uploader: '/AttController/Attach',
swf: 'your/path/to/uploadify.swf',
cancelImg: 'your/path/to/cancel.jpg',
buttonText: 'Select File',
fileSizeLimit: '300KB',
fileTypeDesc: 'Image Files',
fileTypeExts: '*.gif; *.jpg; *.png',
auto: 'true',
multiple: 'false',
onError: function(type, info) {
},
onUploadSuccess: function(file, data, response) {
}
});
Then just change your controller action to
public virtual ActionResult Upload(HttpPostedFileBase FileData)
The FileData would have things like the FileName and would also have the file in an input stream.
Related
I am trying to implement zip file upload functionality in Angular 8 app. 3 conditions that I need to satisfy are:
1. Only allow zip files to be uploaded else throw error message
2. File size should not cross 3 MBs else throw error message
3. When I choose zip file, it should show progress bar but file should only be uploaded via REST API call when I click 'Register' button separately.
What I have implemented so far is:File Upload Service
postFile(fileToUpload: File, header): Observable<any> {
const endpoint = 'your-destination-url';
const formData: FormData = new FormData();
formData.append('fileKey', fileToUpload, fileToUpload.name);
if (fileToUpload.size <= 3048576)
return this.httpClient.post(endpoint, formData, { headers: header })
.pipe(map(data => {
console.log(data);
return data;
},error => {
console.log(error, 'reduce file size');
}))
}
Component TS File
handleFileInput(files: FileList) {
this.fileToUpload = files.item(0);
}
uploadFileToActivity() {
this.fileUploadService.postFile(this.fileToUpload, this.headers).subscribe(data => {
// do something, if upload success
console.log('the file has been uploaded successfully', data);
}, error => {
console.log(error);
});
}
Component HTML
<input type="file"
id="file" (change)="handleFileInput($event.target.files)">
Please suggest how can I modify so that my functionality is as described.
for points 1 and 2 you should add a validation function in your code to check both the file extension and the size.
The upload should be possible only if the file passes the validation.
In addition to that, you should probably return some kind of feedback to the user when the validation fails.
You can track the file upload progress (and show a progress bar) adding additional options to the .post method and listening for specific events
return this.httpClient.post(endpoint, formData, {
headers: header,
reportProgress: true,
observe: 'events'
}).pipe(map(event => {
if (event.type === HttpEventType.Response) {
// upload complete
}
if (event.type === HttpEventType.UploadProgress) {
// the event contains information about loaded data
// you can use event.loaded and event.total to display the progress bar
}
}))
I am trying to upload files from Angular 4 app to a JSON API service that accepts base64 strings as file content.
So what I do is - read the file with FileReader.readAsDataURL, then when user confirms the upload I will create a JSON request to the API and send the base64 string of the file I got earlier.
This is where the problem starts - as soon as I do something with the "content" (log it, send it, w/e) the request will be send, but its insanely slow, e.g. 20 seconds for 2MB file.
I have tried:
using ArrayBuffer and manually converting it to base64
storing the base64 string in HTML and retrieving it later
reading the files after user clicks on upload button
using the old client from #angular/common
using plain XHR request
but everything leads to the same result.
I know where the problem lies. But why does it happen? Is it something browser specific or angular specific? Is there a more preferred approach (keep in mind it has to be base64 string)?
Notes:
changing anything in the API is beyond my control
API is fine, sending any file trough postman will finish immediately
Code:
This method runs when user adds file to the dropzone:
public onFileChange(files: File[]) : void {
files.forEach((file: File, index: number) => {
const reader = new FileReader;
// UploadedFile is just a simple model that contains filename, size, type and later base64 content
this.uploadedFiles[index] = new UploadedFile(file);
//region reader.onprogress
reader.onprogress = (event: ProgressEvent) => {
if (event.lengthComputable) {
this.uploadedFiles[index].updateProgress(
Math.round((event.loaded * 100) / event.total)
);
}
};
//endregion
//region reader.onloadend
reader.onloadend = (event: ProgressEvent) => {
const target: FileReader = <FileReader>event.target;
const content = target.result.split(',')[1];
this.uploadedFiles[index].contentLoaded(content);
};
//endregion
reader.readAsDataURL(file);
});
}
This method runs when users clicks save button
public upload(uploadedFiles: UploadedFile[]) : Observable<null> {
const body: object = {
files: uploadedFiles.map((uploadedFile) => {
return {
filename: uploadedFile.name,
// SLOWDOWN HAPPENS HERE
content: uploadedFile.content
};
})
};
return this.http.post('file', body)
}
For sending big files to server you should use FormData to be able to send it as multi-part instead of a single big file.
Something like:
// import {Http, RequestOptions} from '#angular/http';
uploadFileToUrl(files, uploadUrl): Promise<any> {
// Note that setting a content-type header
// for mutlipart forms breaks some built in
// request parsers like multer in express.
const options = new RequestOptions();
const formData = new FormData();
// Append files to the virtual form.
for (const file of files) {
formData.append(file.name, file)
}
// Send it.
return this.http.post(uploadUrl, formData, options);
}
Also don't forget to set the header 'Content-Type': undefined, I've scratched my head over this for hours.
Here is my ajax code in my "ManageUserRoles.cshtml":
//HIT THE DATABASE FOR USERNAME GIVING IT THIS USERNAME
function isUserValid(thisUser) {
$.ajax({
url: "/Roles/isUserValid/" + thisUser,
type: 'POST',
success: handleResultResponseUserName,
error: function (xhr) { alert("Error..."); }
});
}
//handles data back from ajax call
//RESET INPUT IF NO USER IF FOUND IN USER'S TABLE
function handleResultResponseUserName(ResponseObject) {
if (ResponseObject == "no user with this number") {
$('#frmGetRoles').find('input[name="UserName"]').val(null);
}
else {
//DO NOTHING
}
}
Here is my JsonResult in my RolesController:
[HttpPost]
[ValidateAntiForgeryToken]
public JsonResult isUserValid(string username)
{
var name = string.Empty;
var CheckUserExists = (from c in _db.AspNetUsers
where (c.UserName.Equals(username))
select c);
var results = new JsonResult();
if (CheckUserExists.Any())
{
name = CheckUserExists.First().UserName;
}
else
{
name = "no user with this name in database";
}
return Json(name, JsonRequestBehavior.DenyGet);
}
I've got almost exact code working in a different application and I cut and pasted into a new one where I am trying to use it for Roles management.
The json reference is there, and in web.config. But when I put a breakpoint inside the JsonResult, it never stops there and an error is returned from the client javascript (404 resource not found). There is no other json stuff being used in this new app . . .yet.
I can hit f5 and it returns:
http://localhost/StoreMasterSecure/Roles/ManageUserRoles
which is the view that has the button that runs the ajax. It all gets to the ajax call and then nothing happens and Chrome Developer Tools console shows 404 error.
Even if I type in the URL the path, I get the resource not found 404 page:
http://localhost/StoreMaster/Roles/isValidUser/xxxx#xxx.com
(isValidUser is the JsonResult in the controller, in same controller that ManageUserRoles ActionResult exists and works)
To ensure you url's are correctly generated, use the Url.Action() method and pass the data using the ajax data option. Change your ajax to
$.ajax({
url: '#Url.Action("isUserValid", "Roles")', // change this
data: { username: thisUser }, // add this
type: 'POST',
success: handleResultResponseUserName,
error: function (xhr) { alert("Error..."); }
});
You also need to remove the [ValidateAntiForgeryToken] attibute from your controller method since you not passing the token.
Side note: MVC comes with a RemoteAttribute to handle this scenario. Refer How to: Implement Remote Validation in ASP.NET MVC which means you do not need your script.
I'm trying to save JSON data in my Ionic app to the local device storage. I would like to use the ngCordova File plugin. I can't seem to find any tutorials or example apps that use the exact methods they have in the docs.
Has anyone used this plugin before to save JSON data? How did you do it?
ngCordova takes away a lot of the ugliness of writing files using the file writer API.
This example has been adapted from the docs, and uses writeFile(path, file, data, replace) where the path is defined by cordova.file.DIRECTORY_TYPE, file is a string name for the file, data is the string representation of the data (so we will use JSON.stringify()). Replace is a boolean that will simply erase the existing contents of the file.
//Write using cordova.file.dataDirectory, see File System Layout section for more info
var json = {"test": "hello world"}
$cordovaFile.writeFile(cordova.file.dataDirectory, "hello.json", JSON.stringify(json), true)
.then(function (success) {
// success
}, function (error) {
// error
console.log(error); //error mappings are listed in the documentation
});
For a controller, supposing we are using controllerAs syntax it could look something like this:
angular.controller("...",['$cordovaFile' function ($cordovaFile) {
var vm = this;
vm.writeFile = function (fileName) {
ionic.Platform.ready(function(){
// will execute when device is ready, or immediately if the device is already ready.
var json = {"test": "hello world"}
$cordovaFile.writeFile(cordova.file.dataDirectory, "hello.json", JSON.stringify(json), true)
.then(function (success) {
// success
}, function (error) {
// error
console.log(error); //error mappings are listed in the documentation
});
});
};
});
I am trying to do a file upload using angularjs, using angular-file-upload library (https://github.com/danialfarid/angular-file-upload)
Here is my code
// ===============================My HTML File===========================
<input type="file" ng-file-select="onFileSelect($files)">
// ===============================My Controller==========================
var $scope.formObj = {
name: "Test"
};
var fileToUpload;
$scope.onFileSelect = function (file) {
fileToUpload = file[0];
};
// POSt request to /api/items
$scope.addItem = function() {
console.log($scope.formObj);
$scope.upload = $upload.upload({
url: '/api/items',
method: 'POST',
data: { myObj: $scope.formObj },
file: fileToUpload
}).success(function(data, status, headers, config) {
console.log("success");
});
};
// ================================My Backend=============================
// This is the function that will receive POST request to /api/items
exports.create = function(req, res) {
console.log(req.body); // req.body is just an empty object. ==> {}
// apparently, I found all the data to be in req._readableState.buffer[0]
// in the form of a buffer
var buffer = req._readableState.buffer[0];
// trying to console.log the buffer.toString, resulting in something similar to this
// { name: "Test", image: Object }
console.log(buffer.toString());
return res.send(200);
};
So my backend received the formObj with all its properties and values, however, the actual file data itself, whether in the form of buffer, or base64, or whatever, never gets received.
I wonder why. This is my first time working with file uploading, so I don't understand the concept.
Please point me in the right direction
If you are using Latest version of Express, you'd notice that
app.use(express.multipart()); is no longer bundled with express.
So do the following configuration changes. in express.js
var multer = require('multer');
app.use(multer({ dest: './uploads/'}));
You'd find that after doing this you would find the data and file , in req.body req.file respectively.
Hope it helps