I am using a custom section within umbraco for displaying messages sent by users. I want the admins to be able to answer these messages by uploading a file to the message itself. This way I will know which file belongs to which message. I have followed some guides and recommendations most notably this one
file-upload-in-backoffice-custom-section on how to upload files from umbraco backoffice.
Problem with my code however is that it does not seem to send the whole file with the post request just the path of the file which becomes something similar to: C:\fakepath\file.pdf
My question would be how do I send the whole file in the post request and how can i catch/convert the file into an HttpPostedFileBase inside of the API function. If I can get the file as HttpPostedFileBase I will know how to upload it to the media section of umbraco.
Code below:
edit.html:
<umb-control-group label="File" description="File to upload">
<input type="file" class="umb-editor umb-textstring" ng-model="files" ng-change="fileSelected(files)" ng-multiple="false" />
</umb-control-group>
</div>
</div>
</div>
</umb-editor-container>
<umb-editor-footer>
<umb-editor-footer-content-right>
<div class="umb-button ng-scope">
<button ng-click="OnSave()" class="btn umb-button__button btn-success umb-button--">
<span class="umb-button__content">
Save
</span>
</button>
</div>
</umb-editor-footer-content-right>
</umb-editor-footer>
edit.controller.js:
$scope.fileSelected = function (files) {
$scope.file = files;
};
$scope.OnSave = function () {
var request = {
file: $scope.file
};
return $http({
method: 'POST',
url: "backoffice/Messages/MessagesApi/PostSaveFile",
headers: { 'Content-Type': undefined },
transformRequest: function (data) {
var formData = new FormData();
formData.append("file", data.file);
return formData;
},
data: request
}).then(function (response) {
if (response) {
var fileName = response.data;
return fileName;
} else {
return false;
}
});
};
MessagesApiController.cs:
public HttpResponseMessage PostSaveFile(HttpPostedFileBase file)
{
var files = HttpContext.Current.Request;
if (files != null && files.ContentLength > 0)
{
StreamReader reader = new StreamReader(files.InputStream);
string responseFromServer = reader.ReadToEnd();
}
return Request.CreateResponse(HttpStatusCode.OK);
}
If anyone is stumbling accross this issue this is how I managed to solve it:
edit.html:
<umb-control-group label="Response File" description="File to upload">
<input type="file" id="myFile" ng-multiple="false" />
</umb-control-group>
edit.controller.js:
if ($element.find('#myFile')[0].files[0] !== undefined) {
var fd = new FormData();
fd.append('file', $element.find('#myFile')[0].files[0]);
$http.post("backoffice/Messages/MessagesApi/PostSaveFile", fd, {
transformRequest: angular.identity,
headers: { 'Content-Type': undefined }
}).success(function (response) {
console.log(response);
});
}
MessagesApiController.cs:
public string PostSaveFile()
{
var file = HttpContext.Current.Request.Files["file"];
return file.FileName;
}
Variable file is now an HttpPostedFile and you can use it for whatever you want like saving it into the media section.
For those of you who are struggling with file uploads in custom sections in Umbraco 8 here's my solution:
View:
<input type="file" class="umb-textstring" ngf-select="" ng-model="files" ng-multiple="false" ngf-change="fileSelected(files)" required />
<umb-button action="vm.clickUploadButton()"
type="button"
button-style="info"
label="Upload">
</umb-button>
Controller:
function Controller($scope, Upload) {
var vm = this;
vm.clickUploadButton = clickUploadButton;
$scope.fileSelected = function (files) {
$scope.file = files;
};
function clickUploadButton() {
if (!$scope.file)
return false;
Upload.upload({
url: "backoffice/api/Upload/UploadFile",
fields: {
"field1": "value1",
"field2": "value2"
},
file: $scope.file
}).success(function (data, status, headers, config) {
console.log(status);
console.log(data);
}).error(function (evt, status, headers, config) {
console.log(evt.Message);
});
}
}
API controller:
public class UploadController : UmbracoAuthorizedApiController
{
[HttpPost]
public HttpResponseMessage UploadFile()
{
var files = HttpContext.Current.Request.Files;
var field1 = HttpContext.Current.Request.Form["field1"];
var field2 = HttpContext.Current.Request.Form["field2"];
return Request.CreateResponse(HttpStatusCode.OK, new { fileCount = files.Count, field1, field2 }) ;
}
}
Related
I am trying to upload file using Angular and .Net, below is code which I have used.
I am getting error like file field is required, but I am uploading file but still getting error.
errors: {file: ["The file field is required."]}
file: ["The file field is required."]
status: 400
title: "One or more validation errors occurred."
type: "https://tools.ietf.org/html/rfc7231#section-6.5.1"
Below is .Net side code.
[HttpPost("weeklyproductionreports/uploadfilesnew")]
public async Task<IActionResult> UploadWeeklyProductionReportsFiles([FromForm] IFormFile file) =>
Ok(await _companyService.UploadWeeklyProductionReportsFiles(file));
Below is Html code.
<div class="myfilebrowser">
<input type="file" #UploadFileInput id="fileUpload" (change)="fileChangeEvent($event)" name="fileUpload"
multiple="multiple"
accept="application/pdf,application/vnd.ms-excel,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" />
</div>
Below is ts file code.
fileChangeEvent(fileInput: any) {
if (fileInput.target.files && fileInput.target.files[0]) {
this.file = fileInput.target.files[0];
}
}
upload() {
this.isSubmitting = true;
let formData = new FormData();
formData.append('file', this.file, this.file.name);
this.apiService.uploadWeeklyProductionReportFile(formData).subscribe(
(data) => {
this.isSubmitting = false;
if (data.statusCode == 200) {
console.log(data);
this.uploadedFileUrl = data.data[0];
this.snackBar.open('File uploaded!', 'Ok', {
duration: 2000,
});
this.isShowFileUpload = true;
} else {
this.snackBar.open('File Upload Error: Please Try again', 'Ok', {
duration: 2000,
});
}
},
(error) => {
this.isSubmitting = false;
console.log(error);
this.snackBar.open('File Upload Error: Please Try again', 'Ok', {
duration: 2000,
});
}
);
console.log('Upload works');
}
below is API calling code.
uploadWeeklyProductionReportFile(body: FormData): Observable<any> {
const url = this.baseURL + '/company/weeklyproductionreports/uploadfilesnew';
return this.http.post<any>(url, body, { headers: { 'content-type': 'multipart/form-data' } });
}
So, I would start by changing your upload() method and adding an input parameter:
upload(files: Array<any>)
And where you append your form data I would do the following:
formData.append(files[0].name, files[0])
I have to write a service using Web API. In that method I should receive 2 items:
An Id,
File(s)
This method is going to be called from a mobile app. After googling on the subject, in a sample web page, I converted uploaded file to base 64 (using jQuery) and I received it on the server:
<script type="text/javascript">
// Holds the load event results
var base64 = "";
//
function converToBase64() {
var file = document.querySelector('input[type=file]').files[0];
var reader = new FileReader();
// Event handler
reader.addEventListener("load", function () {
base64 = reader.result;
console.log(base64);
}, false);
// Fires the load event
if (file) {
reader.readAsDataURL(file);
}
}
// AJAX request
$(function () {
$('#Button1').click(function () {
var data = {};
data.Id = "3";
data.File = base64;
console.log(data);
alert("start");
$.ajax({
type: "POST",
url: "/api/SaveFile",
data: JSON.stringify(data),
contentType: "application/json; charset=utf-8",
dataType: "json"
}).done(function (result) {
console.log(result);
});
})
});
</script>
<div>
<input type="file" onchange="converToBase64()"><br>
<input id="Button1" type="button" value="Submit" />
</div>
I have no idea whether my solution is correct. Anyway, after receiving on server side, I converted base 64 string to byte[].
[HttpPost]
public HttpResponseMessage Post(FileUploadData data)
{
string converted = data.File.Substring(data.File.IndexOf(",") + 1, data.File.Length - data.File.IndexOf(",") - 1);
byte[] fileBytes = Convert.FromBase64String(converted);
// Do checking
return Request.CreateResponse(HttpStatusCode.OK, "Success");
}
public class FileUploadData
{
public string Id { get; set; }
public string File { get; set; }
}
I've read lots of solutions to same problem about saving bytes into files. But I should not do this before checking extension and size. As it is dangerous to save banned files on physical space, So how can I do these checking before saving on the disk(as a file)? Because .
View code
<input type="text" id="txtImageName"></input>
<input type="text" id="txtImageDescription"></input>
<input type="file" id="txtImageName"></input>
<input type="button" id="btnUpload" Value="Upload">
Jquery Code
<script>
$('#btnUpload').click(function () {
var file = $("#txtImageName").get(0).files;
$.ajax({
type: 'post',
url: "#Url.Action("HomeAddNew", "Admin")",
data:
{
ImageName: $('#txtImageNameoption').val(),
ImageDescription:$('#txtImageDescription').val(),
Image: file
},
success: function (data) {
alert('hai');
},
error: function (data) {
alert('error');
}
});
});
</script>
Controller code
public ActionResult HomeAddNew(string ImageName, string ImageDescription, HttpPostedFileBase Image)
{
//in the above HttpPostedFilebase I'm getting the null value but, I don't want to use .fileupload method or formdata class in jquery.
return View();
}
Explanation: Whenever I check the file in controller method it is showing as null. Please send me the perfect solution for this. It will help me a lot. My email id is ganesh#concai.co.uk
You can't upload a file with ajax. If your browser requirements starts from IE10then you can use xhr.
Code and information about how you should do it HERE.
Also, there's a plugin called FileAPI, which you can find HERE
Simple demo with progress bar
function startUpload() {
var fileInput = document.getElementById("fileInput");
if (fileInput.files.length == 0) {
alert("Please choose a file");
return;
}
var progressBar = document.getElementById("progressBar");
var xhr = new XMLHttpRequest();
xhr.upload.onprogress = function(e) {
var percentComplete = (e.loaded / e.total) * 100;
progressBar.value = percentComplete;
};
xhr.onload = function() {
if (xhr.status == 200) {
alert("Sucess! Upload completed");
} else {
alert("Error! Upload failed");
}
};
xhr.onerror = function() {
alert("Error! Upload failed. Can not connect to server.");
};
progressBar.value = 0;
xhr.open("POST", "ajax-upload", true);
xhr.setRequestHeader("Content-Type", fileInput.files[0].type);
xhr.send(fileInput.files[0]);
}
I have a client built in Angular. I am trying to upload files that are to be processed by the asp.net server side code. Although I have managed to get files in the file control but I don't know how I can pass the binary data to server side. My client side code looks as follows
HTML Code
<div>
<form name="form1" method="POST" enctype="multipart/form-data">
<div>
{{repopulatecftcModel.title}}
</div>
<div style="padding-top:15px;padding-bottom:15px;"><b>Toolkit - Repopulate Cftc Data</b></div>
<div>
<input type="file" id="updCftcFileUploader" name="files[]" multiple />
</div>
<div>
<input type="button" value="Upload Files" title="Upload Files" ng-click="UploadCFTCFiles()" />
</div>
<div>
<label ng-model="repopulatecftc.validationtext"></label>
</div>
<div>
{{repopulatecftcModel.validationtext}}
</div>
</form>
Controller Code in Angular
function controller($scope, $http, AppModel, WebFunctionService) {
$scope.UploadFiles = function (evt) {
var files = document.getElementById('updFileUploader').files;
for(var i = 0; i < files.length; i++) {
var file = files[i];
if (file) {
// getAsText(file);
alert("Name: " + file.name + "\n" + "Last Modified Date :" + file.lastModifiedDate);
}
}
var parameters = { directory: $scope.defaultsSaveDirectory, filename: "" };
WebFunctionService.promiseWebMethod('UploadFiles', angular.fromJson(parameters), true)
.success(function () {
$scope.userMessage = "File(s) successfully uploaded";
console.log($scope.userMessage);
})
.error(function (error) {
$scope.userMessage = "ERROR uploading files" + error;
console.log($scope.userMessage);
});
};
};
Server side code where I want to access the uploaded files from
[Route("UploadFiles")]
[HttpPost]
public void UploadFiles()
{
}
When I run the code I do get alerts for each of the file being uploaded. Then the code get into the server side as shown in the image below
Its here that I want to access the files from. I have seen in the web where they show Request.Files shows the collection of files being uploaded but when I try to do that the compiler starts complaining.
Anyone have any clues as to how I should be able to pass binary data being uploaded from client side in this case and access it from the server side
In angular when you call the server side you can use $upload to upload a file here is an example:
var uploadFile = function (file, args) {
var deferred = $q.defer();
$upload.upload({
url: "<your url goes here>",
method: "POST",
file: file,
data: args
}).progress(function (evt) {
// get upload percentage
console.log('percent: ' + parseInt(100.0 * evt.loaded / evt.total));
}).success(function (data, status, headers, config) {
// file is uploaded successfully
deferred.resolve(data);
}).error(function (data, status, headers, config) {
// file failed to upload
deferred.reject();
});
return deferred.promise;
}
The above function will pass the file along with extra args if you need it.
Server side
[HttpPost]
public async Task<HttpResponseMessage> AddFile()
{
if (!Request.Content.IsMimeMultipartContent())
{
this.Request.CreateResponse(HttpStatusCode.UnsupportedMediaType);
}
string root = HttpContext.Current.Server.MapPath("~/temp/uploads");
var provider = new MultipartFormDataStreamProvider(root);
var result = await Request.Content.ReadAsMultipartAsync(provider);
// On upload, files are given a generic name like "BodyPart_26d6abe1-3ae1-416a-9429-b35f15e6e5d5"
// so this is how you can get the original file name
var originalFileName = GetDeserializedFileName(result.FileData.First());
var uploadedFileInfo = new FileInfo(result.FileData.First().LocalFileName);
string path = result.FileData.First().LocalFileName;
//Do whatever you want to do with your file here
return this.Request.CreateResponse(HttpStatusCode.OK, originalFileName );
}
private string GetDeserializedFileName(MultipartFileData fileData)
{
var fileName = GetFileName(fileData);
return JsonConvert.DeserializeObject(fileName).ToString();
}
public string GetFileName(MultipartFileData fileData)
{
return fileData.Headers.ContentDisposition.FileName;
}
Using the helpful information I found here:
How can I upload files asynchronously?
I was able to get form data to the server-side with the following jQuery (very slightly modified from the link above):
$('#addFileInput').change(function () {
var file = this.files[0];
name = file.name;
size = file.size;
type = file.type;
//Your validation
});
$('.submitFile').click(function () {
var formData = new FormData($("#fileUploadForm"));
$.ajax({
url: '/AJAX Pages/Compute_File_Upload.cshtml', //Server script to process data
type: 'POST',
xhr: function () { // Custom XMLHttpRequest
var myXhr = $.ajaxSettings.xhr();
if (myXhr.upload) { // Check if upload property exists
myXhr.upload.addEventListener('progress', progressHandlingFunction, false); // For handling the progress of the upload
}
return myXhr;
},
//Ajax events
beforeSend: function () {
$("#progressBar").css("visibility", "visible");
},
success: function (response) {
$(".editLabelTitle").text(response);
},
//error: errorHandler,
// Form data
data: formData,
//Options to tell jQuery not to process data or worry about content-type.
cache: false,
contentType: false,
processData: false
});
});
function progressHandlingFunction(e) {
if (e.lengthComputable) {
$('progress').attr({ value: e.loaded, max: e.total });
}
}
Here's the HTML that is involved:
<div class=\"addFileBox\">
<div class=\"editPageSubTitle dragHandle\">
Add File
<button id=\"closeAddFileBox\">X</button>
</div>
<div class=\"innerAddFileDiv\">
<form id=\"fileUploadForm\" enctype=\"multipart/form-data\">
<input id=\"addFileInput\" name=\"addFileInput\" type=\"file\" />
</form>
<br/>
<progress id=\"progressBar\"></progress>
<br/>
<button class=\"submitFile\">Submit File</button>
</div>
</div>
The Ajax request in and of itself works fine. The problem comes when I don't know how to get the file on the server-side code (normally I would just find the input with the Request.Files["someFileId"]) but as all formData is sent, this isn't working the way I am familiar with.
C# CODEBEHIND
#{
Layout = "";
if(IsAjax)
{
var file = Request.Files["addFileInput"];
var fileName = Path.GetFileName(file.FileName);
var path = Path.Combine(Server.MapPath("~/CMS Files/UtilityBilling"), fileName);
file.SaveAs(path);
}
}
What is the proper way to access the given file, considering my scenario and environment?
Try this from codebehind:
HttpFileCollection filesCollection = HttpContext.Current.Request.Files;
var fileName = filesCollection[0];
string filePath = Path.Combine(HttpContext.Current.Server.MapPath("~/SaveDir"), fileName.FileName);
fileName.SaveAs(filePath);