File upload using AngularJS -> .NET Web Api 2 -> SQL server - angularjs

I'm looking for a solution where I can upload any file to SQL server from an AngularJS frontend to .Net Web Api 2 and straight to SQL Server Database. I've done some research and for angularjs i'm mainly looking at ng-file-upload. my problem is most of the solutions that i've looked at saves the file into a temp folder. I'm not sure if it's possible but I want it straight to an SQL server table.
I've seen some solutions where it converts the file into a byte array which can be saved to an SQL table but I'm not sure how to do this in a .NET web api 2 and from an angularjs front end. thank you in advance.

Don't save files to SQL server--that's not what it's for. See this answer: In MVC4, how do I upload a file (an image) to SQL Server that's part of my domain model? And this answer: Storing files in SQL Server
Uploading files in angular is easy. Do it like this:
Controller
$scope.uploadFile = function() {
//get the filename from the <input type='file'>
//angular doesn't allow attaching ngModel to file input
var fileInput = document.getElementById("myInputId");
//check if there's a file
if(fileInput.files.length === 0) return;
//you cannot send a file as JSON because json is in the string format
//for fileuploads, you must send as a FormData() object
//C# accepts HttpPostedFileBase as the file argument
var file = fileInput.files[0];
//put the file in a new formdata object
var payload = new FormData();
payload.append("file", file);
//upload file to C# controller
$http.post("path/to/C#/controller", payload, {
//you **need** to specify these options, without them upload does not work
transformRequest: angular.identity,
headers: { "Content-Type": undefined }
}).then(function(data) {
//success
}, function(error) {
//error
});
}
C#/ASP.NET
[WebMethod]
public string UploadFile(HttpPostedFileBase file) {
//access the file object here
var inputStream = file.InputStream;
var fileName = Path.GetFileName(file.FileName);
try
{
file.SaveAs("local/path" + fileName);
}
catch (IOException exc)
{
return "Error: " + exc.Message;
}
return "success";
}

Related

s it possible to implement file upload function?

We are implementing file download function.
UI, service API server, and file server (external server) exist, and I cannot access the file server. (Code cannot be changed)
Currently, a file is downloaded by requesting UI -> FileServer.
I want to configure UI -> Service API -> File Server for multiple file downloads and exception handling.
As the service API operates in the middle, the final value of http that the client receives is different, but I don't know why.
Existing file servers returned an ArrayBuffer,
It was changed to a long string in String format while going through my service API server.
// my Server -> fileServer (GET Http Request)
// setting header...
// setting uri...
HttpEntity req = new HttpEntity<>(httpHeaders);
RestTemplate rt = new RestTemplate();
HttpEntity<byte[]> result = rt.exchange(
uri,
HttpMethod.GET,
req,
byte[].class
);
byte[] blob = result.getBody();
The reason why I received byte[] through result.getBody() is that I thought that the return value of the file server would be a file, and it would be a byte[] type in Java.
// react (client code)
// blobFromApi = The result my server responded
let blob = new Blob([blobFromApi], { type:'image/png' });
if (navigator.userAgent.match('CriOS')) {
let reader = new FileReader();
reader.onload = function (e) {
window.location.href = reader.result;
};
reader.readAsDataURL(blob);
} else {
FileSaver.saveAs(blob, file.file_name);
}
});
In conclusion, is it possible to implement a file download function in my current situation?

Downloading an Excel file causes it to corrupt

I have a simple service on Angular 2 and Typescript that requests Excel files to a server and then opens a download file dialogue for the user. However, as it is currently, the file becomes corrupt when downloaded.
When downloaded, it opens fine in OpenOffice and derivates, but throws a "File is Corrupt" error on Microsoft Excel, and asks if the user wants to recover as much as it can.
When Excel is prompted to recover the file, it does so successfully, and the recovered Excel has all rows and data that is expected for the Excel file. Comparing the recovered file against opening the file in OpenOffice and derivates evidence no outstanding differences.
The concrete Excel I am trying to download is generated with Apache POI in a microservice, then passed to the main backend and finally served to the frontend for the user to download. Both the backend and microservice are written in Java, through Spark Framework.
I made some tests on the backends, and concluded the problem is not the report generation nor the data transfer:
Asking the microservice to save the generated Excel in a file within the server and then opening such file (hereby file A) in Excel shows that file A is not corrupted.
Asking the main backend server to save the Excel file that it receives from the microservice in a file within itself and then opening such file in Excel (hereby file B) shows that file B is not corrupted.
Downloading both file A and file B through FileZilla from their respective servers yields completely uncorrupted files.
As such, I believe it is safe to assume the Excel becomes corrupted somewhere between the time the file is received on the frontend and the time the user downloads such file. Additionally, the Catalina logs do not evidence any error that might potentially be happening.
I have read several posts that deal with the issue, including a bug report (https://github.com/angular/angular/issues/14083) that included a workaround via XMLHTTPRequest. However, none of the workarounds detailed were successful in solving my issue.
Attached is the code I am using to both obtain the Excel file from the backend and serve it to the user. I am including both an XMLHTTPRequest and an Angular http call (within comments) since those are the two main ways I have been trying to make this work. Additionally, please do take into account the code has been altered to remove information I do not wish to make public.
download(body) {
let reply = Observable.create(observer => {
let xhr = new XMLHttpRequest();
xhr.open('POST', 'URL', true);
xhr.setRequestHeader('Content-type', 'application/json;charset=UTF-8');
xhr.setRequestHeader('Accept', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
xhr.setRequestHeader('Authorization', 'REDACTED');
xhr.responseType = 'blob';
xhr.onreadystatechange = function () {
if(xhr.readyState === 4) {
if(xhr.status === 200) {
var contentType = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
var blob = new Blob([xhr.response], { type: contentType });
observer.next(blob);
observer.complete();
}
else {
observer.error(xhr.response);
}
}
}
xhr.send(JSON.stringify(body));
});
return reply;
/*let headers = new Headers();
headers.set("Authorization", 'REDACTED');
headers.set("Accept", 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
let requestOptions :RequestOptions = new RequestOptions({headers: headers, responseType: ResponseContentType.Blob});
return this.http.post('URL', body, requestOptions);*/
}
Hereby is the code to prompt the user to download the Excel. It is currently made to work with the XMLHTTPRequest. Please do note that I have also attempted to download without resorting to FileSaver, with no luck.
downloadExcel(data) {
let body = {
/*REDACTED*/
}
this.service.download(body)
.subscribe(data => {
FileSaver.saveAs(data, "Excel.xlsx");
});
}
Hereby are the versions of the tools I am using:
NPM: 5.6.0
NodeJs: 8.11.3
Angular JS: ^6.1.0
Browsers used: Chrome, Firefox, Edge.
Any help on this issue would be appreciated. Any additional information you may need I will be happy to provide.
I think what you want is CSV format which open in Excel, update your sevice as follow:
You should tell Angular you are expecting a response of type blob (Binary Large Object) that is your Excel/Csv file.
Also make sure the URL/API on your server is set to accept content-type='text/csv'.
Here's an example with Angular 2.
#Injectable()
export class YourService {
constructor(private http: Http) {}
download() { //get file from the server
this.http.get("http://localhost/..", {
responseType: ResponseContentType.Blob,
headers: new Headers({'Content-Type', 'text/csv'})
}).subscribe(
response => {
var blob = new Blob([response.blob()], {type: 'text/csv'});
FileSaver.saveAs(blob, 'yourFileName.csv');
},
error => {
console.error('something went wrong');
}
);
}
}
Have you tried uploading/downloading your xls file as base64?
var encodedXLSToUpload = 'data:application/xls;base64,' + btoa(file);
Check this for more details: Creating a Blob from a base64 string in JavaScript

AngularJS GET receives empty reply in Chrome but not in Fiddler

I'm implementing file download using AngularJS and WCF. My back-end is a .NET project hosted in IIS. The file is serialized as an array of bytes and then on the client side I utilize the File API to save the content.
To simplify the problem, back-end is like:
[WebInvoke(Method = "GET", UriTemplate = "FileService?path={path}")]
[OperationContract]
public byte[] DownloadFileBaseOnPath(string path)
{
using (var memoryStream = new MemoryStream())
{
var fileStream = File.OpenRead(path);
fileStream.CopyTo(memoryStream);
fileStream.Close();
WebOperationContext.Current.OutgoingResponse.Headers["Content-Disposition"] = "attachment; filename=\"Whatever\"";
WebOperationContext.Current.OutgoingResponse.ContentType = "application/octet-stream"; // treat all files as binary file
return memoryStream.ToArray();
}
}
And on client side, it just sends a GET request to get those bytes, converts in into a blob and save it.
function sendGetReq(url, config) {
return $http.get(url, config).then(function(response) {
return response.data;
});
}
Save the file then:
function SaveFile(url) {
var downloadRequest = sendGetReq(url);
downloadRequest.then(function(data){
var aLink = document.createElement('a');
var byteArray = new Uint8Array(data);
var blob = new Blob([byteArray], { type: 'application/octet-stream'});
var downloadUrl = URL.createObjectURL(blob);
aLink.setAttribute('href', downloadUrl);
aLink.setAttribute('download', fileNameDoesNotMatter);
if (document.createEvent) {
var event = document.createEvent('MouseEvents');
event.initEvent('click', false, false);
aLink.dispatchEvent(event);
}
else {
aLink.click();
}
setTimeout(function () {
URL.revokeObjectURL(downloadUrl);
}, 1000); // cleanup
});
}
This approach works fine with small files. I could successfully download files up to 64MB. But when I try to download a file larger than 64MB, the response.body is empty in Chrome. I also used Fiddler to capture the traffic. According to Fiddler, Back-end has successfully serialized the byte array and returned it. Please refer to the screenshot below.
In this example, I was trying to download a 70MB file:
And the response.data is empty:
Any idea why this is empty for file over 70MB? Though the response itself is more than 200MB, I do have enough memory for that.
Regarding to the WCF back-end, I know I should use Stream Mode when it comes to large files. But the typical use of my application is to download files less than 10MB. So I hope to figure this out first.
Thanks
Answer my own question.
Honestly I don't know what's going wrong. The issue still persists if I transfer it as a byte array. I eventually gave up this approach by returning a stream instead. Then on the client side, adding the following configuration
{responseType : blob}
and save it as a blob.

Download file with NodeJS

I am creating a csv file with node js and now want to download it to user's browser's default download location. We are using seneca with nodejs and csv file is getting saved on the server. Now when use will click export on the front end which angular based, node will create a csv and download that to user machine. How can we achieve this?
It is possible to download dynamic files generated with Node.js to the browser's default download location. There are many posts outlining how to retrieve static files from the server using the Express helper res.download(). Specific to your question there is a way to achieve what you are asking.
With interpretation of your question the following process is followed:
User generated data is sent to the server for processing when a user clicks export.
Node processes the data and generates a file that is to be downloaded without a second user interaction (user clicks Export and the file is downloaded).
Client
//Export button
$("#exportBtn").click(function () {
//Code to generate data for the CSV and save it to the src variable
var src = csvData;
//Send the CSV data to Node for processing and file generation.
$.post("http://localhost:3000/submitcsv", { csv: src }, function (data) {
//Check the returned file name in data - this check depends on your needs; ie: regex
if (data) {
//request the file that is to be downloaded to the client. Optionally use $window.open()
window.location.href = "http://localhost:3000/app/" + data;
}
});
});
Server
//Post data from the client
app.post('/submitcsv', function (req, res) {
var async = require('async');
var fs = require('fs');
var path = require('path');
var csvData = req.body.csv;
function processCSV(callback) {
//Code to create the csv file and a uniqueIdentifier
callback();
}
function finalize() {
//Save the CSV to the server. This is a specific location on the server in /app.
//You can use Express.static paths that suit your setup.
fs.writeFile('./app/temp/csvFile' + uniqueIdentifier + '.csv', buf, function (err) {
if (err) {
console.log(err);
res.send(500, "Something went wrong...");
}
else {
console.log("CSV file is saved");
//Send the file name and location back to the client
res.send('/temp/csvFile' + uniqueIdentifier + '.csv');
}
});
}
// After the CSV data is processed, save the file and send it to the client.
async.series([
processCSV
], finalize);
});
//Send the requested file back to the client
app.get('./app/:csvFile', function (req, res){
var c = req.params.csvFile;
res.download(c);
//Code to delete the file if it is temporary via fs.unlink
});
While it is not shown in the above code for simplicty, it is recommended that the /app routes sending and receiving this type of data are secured in some manner. Using Passport OIDCStrategy for the protected routes will look something like:
app.all('/app/*', function(req, res, next) {
//Check that the user has previously been authenticated
if (req.user){
next();
} else {
// require the user to log in
res.redirect("/login");
}
});
I dont think you can... Nodejs is serverside... so anything you put under Nodejs, will go to the server...
If the USER click on some button or some other event triggers a process that generates the CSV file, then the USER will have to choose where they want to save it or the file will be downloaded automatically to the default Downloads directory that was specified under USER's browser settings...

Read a file and display its data and then upload it. : Angularjs

I need to read a xls file and display its data onto the client and then if the user selects, upload it to the server.
i have till now tried :
var f = document.getElementById('file').files[0],
r = new FileReader();
r.onloadend = function(e){
var data = e.target.result; console.log(data);
//send you binary data via $http or $resource or do anything else with it
}
r.readAsDataURL(f);
}
But now i am getting data as :
data:application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;base ....\
which is a downloadable file.
How can i read the data of a file on the client side using Angularjs and then selectively upload and save it onto the server.

Resources