Not receiving content headers from Web API to Angular - angularjs

I have a Web API that I want to return a file and the file details.
HttpResponseMessage result = Request.CreateResponse(HttpStatusCode.OK);
var file = new FileStream(filePath, FileMode.Open, FileAccess.Read);
result.Content = new StreamContent(file);
result.Headers.Add("filename", "MyFile");
return result;
Within Angular I do the following:
$http.get(url).then(function(response) {
console.log(response.headers());
});
The response.headers() does not contain my header record.
What am I missing?
I've looked at other examples online and they are like this.

I needed to add the following line and change the name to x-filename
result.Headers.Add("Access-Control-Expose-Headers", "x-filename");

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?

Rename file saved to the file system with angular 2

I want to save a blob downloaded from a server onto the file system, so i use
window.open( window.URL.createObjectURL(myBlob));
to save it. But when i do that, then the saved file gets a completly jumbled random name.
Is there any way to give the saved file my own name?
EDIT:
Server side looks like this (Spring Data REST controller):
#RequestMapping(value = "/downloadFile/{fileName}", method = RequestMethod.GET)
public void downloadFile(#PathVariable(value="fileName") String fileName, HttpServletResponse response) throws IOException {
File file = getMyFile(fileName)
response.addHeader("Content-Disposition", "attachment; filename="+file.getName());
response.setContentType(Files.probeContentType(file.toPath()));
response.setContentLengthLong(file.length());
response.getOutputStream().write(FileUtils.readFileToByteArray(file));
}
I've been searching for a similar answer myself and found several possibilities to save a downloaded file.
In your case you can use the download attribute of the <a> element:
. You can dynamically add this element when for instance a button is clicked, and programmatically perform the click event.
var a = document.createElement("a"),
fileURL = URL.createObjectURL(blob);
a.style = "display:none";
a.href = fileURL;
a.download = fileName;
window.document.body.appendChild(a);
a.click();
window.document.body.removeChild(a);
URL.revokeObjectURL(fileURL);
The download atrribute only has recent support: http://www.w3schools.com/tags/att_a_download.asp
For older versions of IE, use
if (typeof window.navigator.msSaveBlob !== 'undefined') {
window.navigator.msSaveOrOpenBlob(blob, fileName);
}

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.

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

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";
}

Blob Upload Hanging

I was wondering if anything had changed recently in relation to uploading blobs to appengine from external applications? What used to work perfectly only 3 months ago is now hanging when doing a http post to upload the blob.
The code (see below) which was working fine previously consists in fetching a pull queue from AppEngine (using the REST API), doing some stuff with this task received and then uploading back the result as a Blob on AppEngine. The url to upload the blob to is created by appengine using blobstoreService.createUploadUrl("/upload");
and is of the form:
http://myapp.appspot.com/_ah/upload/AMmfu6aAHnkuS4ngyRJDn7urFFZeBxb_-3P-r7RY9udMvRjLWkEZNJMgUX1DFczNVi-NhIxcFat2AEPXs2IRJ0AOmznSMgcrCKmL7mGAmS7nqtr-UyYFkglD88BwCfzIui9M2yez7DSQ/ALBNUaYAAAAAUGRlEwpeGEc5ozp8Z8sDO33qgCi2AiIE/
I had a look at the logs on AppEngine and it seems like the servlet in charge of /upload isn't being triggered.
I'm honestly out of ideas at this stage, any help would be greatly appreciated ! :-)
Cheers,
Killian
public boolean uploadAsBlob(String dataToWrite, String uploadURL) {
try {
BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(tempFileLocation));
bufferedWriter.write(dataToWrite);
bufferedWriter.newLine();
bufferedWriter.close();
MultipartEntity entity = new MultipartEntity();
entity.addPart(blobFileName, new FileBody(new File(tempFileLocation)));
HttpPost method = new HttpPost(uploadURL);
method.setEntity(entity);
final HttpParams httpParams = new BasicHttpParams();
HttpConnectionParams.setConnectionTimeout(httpParams, 10000);
DefaultHttpClient httpclient = new DefaultHttpClient(httpParams);
//It hangs at the following line!
HttpResponse response = httpclient.execute(method);
if (response.getStatusLine().getStatusCode() == 200) {
logger.info("Uploaded blob to url: " + uploadURL);
return true;
} else {
logger.warning("Couldn't upload blob to url: " + uploadURL);
}
} catch (Exception e) {
logger.warning("Exception " + e.getMessage() + " occured while uploading blob to url:" + uploadURL);
logger.warning("Couldn't upload blob to url: " + uploadURL);
}
return false;
}
I have found that GAE has recently started to keep any GET parameters when invoking blobstoreService.createUploadUrl(). In my case:
http://www.myapp.com/BG?_=1354631578951
With this (unexpected) parameter, the created URL was:
http://www.myapp.com/_ah/upload/?_=1354631578951/AMmfu6YgVPoJzWXdbf70k6J0zdjEeRnnRJ2PYCb3Jgdwk3SqmKEnFyKgy_17CKwiqbC2HyO-FlPVX-C53W0LjHSywaq7YmLegD97uU-GrpWRdBdWbfKf0Dk/ALBNUaYAAAAAUL4L8iDS5E99f3Wky2p59wWpCD84AqoP/
Notice that the '_' parameter is still there. Removing the parameter (or maybe moving from GET to POST) fixed the problem.

Resources