How to download files using axios.post from webapi - reactjs

I have a complex object parameter that I need to send as post, as it could be too long for querystring. The post call is asking to have an excel file dynamically generated and then downloaded asynchronously. But all of this is happening inside of a react application. How does one do this using axios.post, react, and webapi? I have confirmed that the file does generate and the download up to the response does come back, but I'm not sure how to actually open the file. I have a hidden iframe that I'm trying to set the path, src, of the file to, but I dont know what response property to use.
// webapi
[HttpPost]
public HttpResponseMessage Post([FromBody]ExcelExportModel pModel)
{
var lFile = ProductDataModel.GetHoldingsExport(pModel);
var lResult = new HttpResponseMessage(HttpStatusCode.OK);
lResult.Content = new ByteArrayContent(lFile);
lResult.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
FileName = "HoldingsGridExport.xls"
};
lResult.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
return lResult;
}
// client side api
static getHoldingsExport({ UserConfigurationID, UserID, Configurations, ViewName, SortModel, FilterModel, UserConfigType, IsDefault, LastPortfolioSearchID = null, ProductId }) {
const filterModel = JSON.stringify(FilterModel); // saving as string as this model is dynamically generated by grid out of my control
const sortModel = JSON.stringify(SortModel);
let params = JSON.stringify({
UserConfigurationID,
UserID,
Configurations,
ViewName,
filterModel,
sortModel,
UserConfigType,
IsDefault,
LastPortfolioSearchID,
ProductId
});
return axiosInstance.post("/api/HoldingsExport", params);
}
// client side app call to get file
HoldingsApi.getHoldingsExport(config)
.then(function(response) {
debugger;
let test = response;
})
.catch(error => {
toastr.success('Failed to get export.');
});

This is how I've achieved file downloads by POSTing via Axios:
Axios.post("YOUR API URI", {
// include your additional POSTed data here
responseType: "blob"
}).then((response) => {
let blob = new Blob([response.data], { type: extractContentType(response) }),
downloadUrl = window.URL.createObjectURL(blob),
filename = "",
disposition = response.headers["content-disposition"];
if (disposition && disposition.indexOf("attachment") !== -1) {
let filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/,
matches = filenameRegex.exec(disposition);
if (matches != null && matches[1]) {
filename = matches[1].replace(/['"]/g, "");
}
}
let a = document.createElement("a");
if (typeof a.download === "undefined") {
window.location.href = downloadUrl;
} else {
a.href = downloadUrl;
a.download = filename;
document.body.appendChild(a);
a.click();
}
}).catch((error) => {
// ...
});

Just in case the above solution does not serve you quite well, here is how I could be able to download videos that are hosted on S3 AWS buckets,
const handleDownload = () => {
const link = document.createElement("a");
link.target = "_blank";
link.download = "YOUR_FILE_NAME"
axios
.get(url, {
responseType: "blob",
})
.then((res) => {
link.href = URL.createObjectURL(
new Blob([res.data], { type: "video/mp4" })
);
link.click();
});
};
And I trigger handleDownload function in a button with onClick.
The url in the function has the video URL from S3 buckets

Related

React Fetch download a pdf from Java REST API won't read in Adobe

I have tested the API in Postman and the PDF renders fine. So I know the API is working correctly.
When I fetch the PDF from within my React code Adobe gives me the error: "Adobe Acrobat cannot open the because it is neither a supported file type or because the file has been damaged"
My React code:
const downloadFile = async uploadId => {
const response = await callFetch("/uploads/download/" + uploadId + "?officerId=" + officerId, "GET", "");
if (response.status === 401 || response.status === 403) {
alert("Error " + response.status);
sessionStorage.clear();
return;
}
const file = response.blob();
const url = URL.createObjectURL(
new Blob([file], {type:"application/pdf"})
);
const link = document.createElement('a');
link.href = url;
link.setAttribute(
'download',
`FileName.pdf`,
);
// Append to html link element page
document.body.appendChild(link);
// Start download
link.click();
// Clean up and remove the link
link.parentNode.removeChild(link);
URL.revokeObjectURL(url);
};
const callFetch = (endpoint, method, jsonStr) => {
let myHeaders = new Headers();
myHeaders.append("Content-Type", "application/json");
myHeaders.append("Accept","application/json");
return callFetchApi(endpoint, method, jsonStr, myHeaders);
};
const callFetchApi = (endpoint, method, data, myHeaders) => {
const serverName = "http://localhost:8080/AuxPolice/api";
myHeaders.append("Access-Control-Allow-Credentials", 'true');
myHeaders.append("Access-Control-Allow-Origin", '*');
const jwt = sessionStorage.getItem("jwt");
let headerJwt = "Bearer " + jwt;
if (jwt != null) {
myHeaders.append("Authorization", headerJwt);
}
let myInit = {method: method
,headers: myHeaders
};
let url = serverName + endpoint;
if (data) {
myInit.body = data;
}
let returnFetch = fetch(url, myInit);
return returnFetch;
};
Here is my Java code:
#GetMapping(value = "/download" + "/{id}")
public ResponseEntity<Resource> downloadGet(#PathVariable Long id, #RequestParam Long officerId) throws SQLException
{
Officer loggedInOfficer = this.auxPoliceService.getOfficer(officerId);
Upload paramRec = new Upload();
paramRec.setUploadId(id);
Upload download = auxPoliceService.getUploads(loggedInOfficer.getOfficerId(), paramRec).get(0);
Blob blob = download.getBlob();
byte [] bytes = blob.getBytes(1, (int)blob.length());
blob.free();
InputStreamResource resource = new InputStreamResource(new ByteArrayInputStream(bytes));
String filename = download.getFilename();
String contentType = "application/pdf; name=\"" + filename() + "\"";
HttpHeaders headers = new HttpHeaders();
headers.set("content-disposition", "inline; filename=" + filename);
return ResponseEntity.ok()
.headers(headers)
.contentLength(bytes.length)
.contentType(MediaType.parseMediaType(contentType))
.body(resource);
}
Any ideas?

Download zip file from http api in react, Receiving error: "Unable to expand file.zip. It is an unsupported format"

Thanks in advance for taking a look. I am working on being able to download a zip file from react through a django api request. I am able to click on my pop up that downloads the zip file, but when I double click on the zip file to open, I get this error: "Unable to expand file_name.zip. It is an unsupported format" My response with the zip file seems to be passing correctly to the front end, so I am thinking it may be something wrong with the react code when making the "blob"? Thanks again.
Django code:
class DownloadZip(APIView):
def post(self, request, format=None):
# information to find file to create zip
profile_name = request.data["profile"]
profile_year = request.data["year"]
# file path to create zips from
path = str(Path(__file__).parent.resolve())
zip_dir = shutil.make_archive(profile_name + profile_year, "zip", path + "/" + profile_name + profile_year)
s = io.StringIO(zip_dir)
response = HttpResponse(s, content_type = "application/zip")
zip_name = profile_name + profile_year + ".zip"
response["Content-Disposition"] = f"attachment; filename={zip_name}"
return response
React code:
downloadZip = async () => {
const params = {
profile: this.state.profileName,
year: this.state.year,
};
axios({
url: `${serverUrl}/download_zip`,
method: "post",
data: params
}).then(
(res) => {
const url = window.URL.createObjectURL(new Blob([res.data],{type:'application/zip'}));
const link = document.createElement('a');
link.href = url;
link.setAttribute('download', 'file.zip');
document.body.appendChild(link);
link.click();
link.parentNode.removeChild(link);
},
(error) => {
console.log(error);
});
}
I did do a fellow commentor's suggestion, and updated to get route with query params, but am having the same issue. I can double click on the zip link on the web browser but a pop up appears "Unable to expand filename.zip. It is an unsupported format"
Please try adding {responseType: 'arraybuffer'}. I also had the same problem but after adding this {responseType: 'arraybuffer'}. I am getting correct file.
downloadZip = async () => {
const params = {
profile: this.state.profileName,
year: this.state.year,
};
axios.post(
`${serverUrl}/download_zip`,
params,
{
responseType: 'arraybuffer'
}
).then(
(res) => {
const url = window.URL.createObjectURL(new Blob([res.data],{type:'application/zip'}));
const link = document.createElement('a');
link.href = url;
link.setAttribute('download', 'file.zip');
document.body.appendChild(link);
link.click();
link.parentNode.removeChild(link);
},
(error) => {
console.log(error);
});
}

How to download file without opening in new tab in reactjs

I am trying to download file but it is opening in new tab, but here I want to download file directly.
What I have tried
<a href={api_Url+'/SurveyImages/'+link} target = "_blank" download ={link}>{link}</a>
if I remove target = "_blank" then it replacing the file with my application tab.
How can I directly download it?
your code:
target = "_blank"
solution:
target = "_self"
Best solution as per new chrome specification https://developers.google.com/web/updates/2018/02/chrome-65-deprecations
Convert this code in React.
Vanilla JavaScript
public static downloadFile(url: string): void {
const xmlHttp = new XMLHttpRequest();
xmlHttp.onreadystatechange = () => {
if (xmlHttp.readyState === 4 && xmlHttp.status === 200) {
const blobUrl = window.URL.createObjectURL(xmlHttp.response);
const e = document.createElement('a');
e.href = blobUrl;
e.download = blobUrl.substr(blobUrl.lastIndexOf('/') + 1);
document.body.appendChild(e);
e.click();
document.body.removeChild(e);
}
};
xmlHttp.responseType = 'blob';
xmlHttp.open('GET', url, true);
xmlHttp.send(null);
}
If you're using angular try this.
async downloadBrochure(url: string) {
try {
const res = await this.httpClient.get(url, { responseType: 'blob' }).toPromise();
this.downloadFile(res);
} catch (e) {
console.log(e.body.message);
}
}
downloadFile(data) {
const url = window.URL.createObjectURL(data);
const e = document.createElement('a');
e.href = url;
e.download = url.substr(url.lastIndexOf('/') + 1);
document.body.appendChild(e);
e.click();
document.body.removeChild(e);
}
Click to download
This is not supported on all browsers: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a tags.
Try this:
axios({
url: 'http://localhost:5000/static/example.pdf',
method: 'GET',
responseType: 'blob', // important
}).then((response) => {
const url = window.URL.createObjectURL(new Blob([response.data]));
const link = document.createElement('a');
link.href = url;
link.setAttribute('download', 'file.pdf');
document.body.appendChild(link);
link.click();
});
Reference: https://gist.github.com/javilobo8/097c30a233786be52070986d8cdb1743
You can try "HTML download Attribute"
https://www.w3schools.com/tags/att_a_download.asp
Download

Downloading from S3 in node and opening in a new window

My Angular 1 application saves files to S3 and allows for a wide variety of files types.
When I retrieve the objects I use the following code:
export function show(req, res) {
const s3 = new aws.S3();
const s3Params = {
Bucket: S3_BUCKET,
Key: req.query.key + ''
};
res.attachment(req.query.key + '');
var fileStream = s3.getObject(s3Params).createReadStream();
fileStream.pipe(res);
}
I would like to open the received file on the client in a new window (just like on the AWS console) but I can't figure out how to go about it.
For example on the client side does not work at all:
.then(
(data) => {
var file = new Blob([data], {type: 'application/pdf'});
var fileURL = URL.createObjectURL(file);
window.open(fileURL);
}
)
I really don't understand how the concept of data streams works.
If you don't have to download pdf, you may open it directly from s3.
s3client.getResourceUrl("your-bucket", "some-path/some-key.jpg");
This will return you url to the file.
So you need code like:
export function show(req, res) {
this.s3client = new aws.S3({
accessKeyId: options.accessKeyId,
secretAccessKey: options.secretAccessKey,
region: options.region
})
let resourceUrl = s3client.getResourceUrl(S3_BUCKET, req.query.key + '');
window.open(resourceUrl, '_blank');
}
I'm sorry, can't test it right now, but try. Should work.
All I had to do was get a signedUrl for the resource for this to work much simpler than what I was trying to do.
export function show(req, res) {
const s3 = new aws.S3();
const s3Params = {
Bucket: S3_BUCKET,
Key: req.query.key + ''
};
s3.getSignedUrl('getObject', s3Params, (err, data) => {
if (err) {
console.log(err);
return res.end();
}
const returnData = {
signedRequest: data,
};
res.write(JSON.stringify(returnData));
res.end();
});
}
and on the client all I have to do is open the link in a new tab:
openDoc(doc) {
this.$http()
.then(
(data) => {
this.$window.open(data.data.signedRequest, '_blank')
}
)
.catch(
(err) => {
this.Notification.error('failed to download attachment');
}
)
}

Angular ng-file-upload Get Byte Array and FormData in Asp.net MVC

I am trying to use ng-file-upload to upload files using Angular. I need the byte array to store in our database (I cannot store the uploaded file on the server), but I also need the FormData as well. My problem is that I can only seem to get one or the other (either the byte array or the formdata) but not both.
Here is my Angular code:
$scope.uploadPic = function (file) {
$scope.emrDetailID = 7;
file.upload = Upload.upload({
url: '/SSQV4/SSQV5/api/Document/UploadEMRDocument',
method: 'POST',
data: { file: file, 'emrdetail': $scope.emrDetailID}
});
file.upload.then(function (response) {
$timeout(function () {
file.result = response.data;
$scope.imageID = file.result;
});
});
};
Using the code below, I can get the byte array and store it in my database:
public async Task<IHttpActionResult> UploadDocument()
{
var provider = new MultipartMemoryStreamProvider();
await Request.Content.ReadAsMultipartAsync(provider);
var f = provider.Contents.First(); // assumes that the file is the only data
if (f != null)
{
string ClientIP = IPNetworking.GetIP4Address();
var filename = f.Headers.ContentDisposition.FileName.Trim('\"');
filename = Path.GetFileName(filename);
var extension = Path.GetExtension(filename).TrimStart('.');
var buffer = await f.ReadAsByteArrayAsync();
FileImageParameterModel pm = new FileImageParameterModel();
pm.binFileImage = buffer;
//pm.CompanyID = UserInfo.intMajorID;
pm.CompanyID = 10707;
pm.dteDocumentDate = Convert.ToDateTime("4/4/2016");
pm.dteExpiration = Convert.ToDateTime("4/4/2017");
pm.vchUserIP = ClientIP;
pm.vchUploadedbyUserName = UserInfo.Username;
pm.vchFileExtension = extension;
CommonClient = new CommonWebApiClient();
CommonClient.AuthorizationToken = UserInfo.AccessToken;
int imageID = await CommonClient.InsertNewFileImage(pm);
return Json(imageID);
}
else
{
return BadRequest("Attachment failed to upload");
}
}
Using the code below I can get the FormData
var provider = new MultipartFormDataStreamProvider(workingFolder);
await Request.Content.ReadAsMultipartAsync(provider);
var emr = provider.FormData["emrdetail"];
but then I can't get the byte array as using MultipartFormDataStreamProvider wants a folder to store the file.
There's got to be a way to get both. I have been searching the internet for 2 days and all I can find are the two solutions above neither of which solves my issue.
Any assistance is greatly appreciated!
You are thinking way to complicated. Here is some of my code which I used for file upload in AngularJS with .NET
Angular:
function uploadFileToUrl(file) {
var formData = new FormData(); // Notice the FormData!!!
formData.append('uploadedFile', file);
return $http({
url: uploadUrl,
method: 'POST',
data: formData,
headers: {
'Content-Type': undefined
}
}).then(resolve, reject);
function resolve(data) {
$log.debug('data : ', data);
return data;
}
function reject(e) {
$log.warn('error in uploadFileToUrl : ', e);
return $q.reject(e);
}
}
Server:
public Task HandleAsync([NotNull] UploadFilesCommand command)
{
return wrapper.InvokeOnChannel(async client =>
{
// init command
command.Output = new Dictionary<string, int>();
try
{
foreach (var file in command.Files)
{
var request = new UploadFileRequest
{
FileName = file.Name,
FileStream = file.Stream
};
UploadFileResponse response = await client.UploadFileAsync(request);
command.Output.Add(file.Name, response.Id);
}
}
finally
{
// dispose streams
foreach (var file in command.Files)
{
if (file.Stream != null)
{
file.Stream.Dispose();
}
}
}
});
}

Resources