I have an Angular application that downloads a file to open in a new tab. It works perfectly on any plateform except on IOS where nothing happens. I make the call in the subscribe because during the download I display a spinner before opening the document on a new tab
this.myservice.dowloadFile(id).subscribe((res) =>
{
let blob = new Blob([file], {type: "application/pdf"});
const url = window.URL.createObjectURL(blob);
let anchor = document.createElement("a");
anchor.href = url;
anchor.target = '_blank';
anchor.click();
this.loadData = false;
});
}
I don't understand the problem.
Related
I'm trying to open a PDF file with print dialog in IE Edge, it works fine in chrome but not in IE
MVC code to return file using Evo Pdf tool:
var restClient = new RestClient(Request.Url.Scheme + "://" + Request.Url.Authority);
var restResponse = restClient.Execute(request);
if (restResponse.StatusCode == HttpStatusCode.OK)
{
htmlModel.HtmlString = restResponse.Content;
byte[] pdfBytes = PdfUtil.GetEvoPdfBytes(htmlModel);
if (pdfBytes != null)
{
return File(pdfBytes, System.Net.Mime.MediaTypeNames.Application.Pdf, htmlModel.PdfName + ".pdf");
}
}
Javascript code to open file with print dialog, below code works in chrome but not IE:
var req = new XMLHttpRequest();
req.open("POST", "/api/HtmlToPdf", true);
req.setRequestHeader("Content-Type", "application/json");
req.responseType = "blob";
req.onload = function (event) {
var blob = req.response;
console.log(blob.size);
var lin = window.URL.createObjectURL(blob);
// Works in chrome
var mywindow = window.open(lin, "_blank");
mywindow.focus();
mywindow.print();
};
req.send(JSON.stringify(
{
htmlModel: {
ElementSelector: "#div",
PageOrientation: "Portrait",
PdfName: "abc"
}
}));
IE11 does not support URL.createObjectURL(). So your code will not work for IE browser and you will not be able to open the blob with print dialog..
As a work around, you need to use msSaveBlob or msSaveOrOpenBlob for Internet Explorer browser.
These methods allow a user to save the file on the client as if the file had been downloaded from the Internet.
var blobObject = new Blob(["This is sample text..."]);
window.navigator.msSaveOrOpenBlob (blobObject, 'msSaveOrOpenBlob_testFile.txt');
References:
(1) Download a blob from HTTP URL in IE 11
(2) Saving files locally using Blob and msSaveBlob
(3) Blob download is not working in IE
I am implementing direct upload with Shrine, jquery.fileupload and cropper.js
in the add portion I am loading the image from the file upload to modal, define the cropper and show the modal
if (data.files && data.files[0]) {
var reader = new FileReader();
var $preview = $('#preview_avatar');
reader.onload = function(e) {
$preview.attr('src', e.target.result); // insert preview image
$preview.cropper({
dragMode: 'move',
aspectRatio: 1.0 / 1.0,
autoCropArea: 0.65,
data: {width: 270, height: 270}
})
};
reader.readAsDataURL(data.files[0]);
$('#crop_modal').modal('show', {
backdrop: 'static',
keyboard: false
});
}
Then on the modal button click I get the cropped canvas call on it toBlob and submit to S3
$('#crop_button').on('click', function(){
var options = {
extension: data.files[0].name.match(/(\.\w+)?$/)[0], // set extension
_: Date.now() // prevent caching
};
var canvas = $preview.cropper('getCroppedCanvas');
$.getJSON('/images/cache/presign', options).
then(function (result) {
data.formData = result['fields'];
data.url = result['url'];
data.paramName = 'file';
if (canvas.toBlob) {
canvas.toBlob(
function (blob) {
var file = new File([blob], 'cropped_file.jpeg');
console.log('file', file);
data.files[0] = file;
data.originalFiles[0] = data.files[0];
data.submit();
},
'image/jpeg'
);
}
});
});
After the upload to S3 is done I am writing to image attributes to hidden field, closing the modal and destroying the cropper
done: function (e, data) {
var image = {
id: data.formData.key.match(/cache\/(.+)/)[1], // we have to remove the prefix part
storage: 'cache',
metadata: {
size: data.files[0].size,
filename: data.files[0].name.match(/[^\/\\]*$/)[0], // IE returns full path
// mime_type: data.files[0].type
mime_type: 'image/jpeg'
}
};
console.log('image', image);
$('.cached-avatar').val(JSON.stringify(image));
$('#crop_modal').modal('hide');
$('#preview_avatar').cropper('destroy');
}
An chrome everything worked fine from the very beginning, but then I figured out the safari has no toBlob functionality.
I found this one:
https://github.com/blueimp/JavaScript-Canvas-to-Blob
And toBlob is not a function error was gone..
Now I can not save the image due to some mime type related issue.
I was able to find out the exact location where it fails on safari but not chrome.
determine_mime_type.rb line 142
on line 139 in the options = {stdin_data: io.read(MAGIC_NUMBER), binmode: true}
the stdin_data is empty after the io.read
Any ideas?
Thank you!
UPDATE
I was able to figure out that the url to the cached image returned by the
$.getJSON('/images/cache/presign', options)
returns empty file when cropped and uploaded from safari.
So as I mentioned in the question safari uploaded empty file once it was cropped by cropper.js.
The problem clearly originated from this block:
if (canvas.toBlob) {
canvas.toBlob(
function (blob) {
var file = new File([blob], 'cropped_file.jpeg');
console.log('file', file);
data.files[0] = file;
data.originalFiles[0] = data.files[0];
data.submit();
},
'image/jpeg'
);
}
I found in some comment on one of the articles I read that safari does some thing like "file.toString" which in my case resulted in empty file upload.
I appended the blob directly without creating a file from it first and everything worked fine.
if (canvas.toBlob) {
canvas.toBlob(
function (blob) {
data.files[0] = blob;
data.files[0].name = 'cropped_file.jpeg';
data.files[0].type = 'image/jpeg';
data.originalFiles[0] = data.files[0];
data.submit();
},
'image/jpeg'
);
}
I have an asp.net MVC application, below code works file.
But the code is that, When navigate to Email action in browser, an EML file is download, then when we click on that file, the file will open with outlook.
Can it be possible, when action calls, then EML file will directly open with outlook, instead of download and then click to open??
Code
public async Task<FileStreamResult> Email()
{
string dummyEmail = "test#localhost.com";
var mailMessage = new MailMessage();
mailMessage.From = new MailAddress(dummyEmail);
mailMessage.To.Add("dejan.caric#gmail.com");
mailMessage.Subject = "Test subject";
mailMessage.Body = "Test body";
// mark as draft
mailMessage.Headers.Add("X-Unsent", "1");
// download image and save it as attachment
using (var httpClient = new HttpClient())
{
var imageStream = await httpClient.GetStreamAsync(new Uri("http://dcaric.com/favicon.ico"));
mailMessage.Attachments.Add(new Attachment(imageStream, "favicon.ico"));
}
var stream = new MemoryStream();
ToEmlStream(mailMessage, stream, dummyEmail);
stream.Position = 0;
return File(stream, "message/rfc822", "test_email.eml");
}
private void ToEmlStream(MailMessage msg, Stream str, string dummyEmail)
{
using (var client = new SmtpClient())
{
var id = Guid.NewGuid();
var tempFolder = Path.Combine(Path.GetTempPath(), Assembly.GetExecutingAssembly().GetName().Name);
tempFolder = Path.Combine(tempFolder, "MailMessageToEMLTemp");
// create a temp folder to hold just this .eml file so that we can find it easily.
tempFolder = Path.Combine(tempFolder, id.ToString());
if (!Directory.Exists(tempFolder))
{
Directory.CreateDirectory(tempFolder);
}
client.UseDefaultCredentials = true;
client.DeliveryMethod = SmtpDeliveryMethod.SpecifiedPickupDirectory;
client.PickupDirectoryLocation = tempFolder;
client.Send(msg);
// tempFolder should contain 1 eml file
var filePath = Directory.GetFiles(tempFolder).Single();
// create new file and remove all lines that start with 'X-Sender:' or 'From:'
string newFile = Path.Combine(tempFolder, "modified.eml");
using (var sr = new StreamReader(filePath))
{
using (var sw = new StreamWriter(newFile))
{
string line;
while ((line = sr.ReadLine()) != null)
{
if (!line.StartsWith("X-Sender:") &&
!line.StartsWith("From:") &&
// dummy email which is used if receiver address is empty
!line.StartsWith("X-Receiver: " + dummyEmail) &&
// dummy email which is used if receiver address is empty
!line.StartsWith("To: " + dummyEmail))
{
sw.WriteLine(line);
}
}
}
}
// stream out the contents
using (var fs = new FileStream(newFile, FileMode.Open))
{
fs.CopyTo(str);
}
}
}
With Chrome you can make it automatically open certain files, once they are downloaded.
.EML should attempt to open in Outlook.
I am not sure about other browsers, but Chrome seemed to be the only one with this option.
It's not a pefect solution because if someone downloaded an .EML from another website in Chrome, it will open automatically aswell.
I recommend having Chrome dedicated to your Web application.
You sure can open local .eml file with Outlook.
But in context of web application, you must firstly download it.
I am developing an app using Web Audio Api. I have discovered that there is a memory leak in the way that Safari handles audio and doesn't garbage college the Audio Context correctly. For this reason I wish to load a new page. Have that page create the Audio Context, complete the operation and then close the window, so that the memory is released.
I have done the following to achieve this.
ref = window.open('record.html', '_self'); This will open the record.html page in the Cordova WebView according to https://wiki.apache.org/cordova/InAppBrowser
1 window.open('local-url.html');// loads in the
Cordova WebView
2 window.open('local-url.html', '_self');
// loads in the Cordova WebView
The record.html page loads a javascript file, that runs the operations that I wish to run. Here is the recordLoad.js file that makes some calls to native operations ( The native API is only available if loaded in the Cordova Webview and as you can see I need to access the file system, so this is the only way I can see to do it.
window.onload = createAudioContext;
ref = null;
function createAudioContext(){
console.log('createAudioContext');
window.AudioContext = window.AudioContext || window.webkitAudioContext;
navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia;
window.URL = window.URL || window.webkitURL;
audioContext = new AudioContext;
getDirectory();
}
function getDirectory(){
console.log('getDirectory');
window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, getFileSystem, fail);
}
function getFileSystem(directory){
console.log('getFileSystem');
var audioPath = localStorage.getItem('audioPath');
directory.root.getFile(audioPath, null, getVocalFile, fail);
}
function getVocalFile(fileEntry){
console.log('getVocalFile');
fileEntry.file(readVocalsToBuffer, fail);
}
function readVocalsToBuffer(file){
console.log('readVocalsToBuffer');
var reader = new FileReader();
reader.onloadend = function(evt){
var x = audioContext.decodeAudioData(evt.target._result, function(buffer){
if(!buffer){
console.log('error decoding file to Audio Buffer');
return;
}
window.voiceBuffer = buffer;
buffer = null;
loadBuffers();
});
}
reader.readAsArrayBuffer(file);
}
//web
function loadBuffers(){
console.log('loadBuffers');
var srcSong = localStorage.getItem('srcSong');
try{
var bufferLoader = new BufferLoader(
audioContext,
[
"."+srcSong
],
createOffLineContext
);
bufferLoader.load()
}
catch(e){
console.log(e.message);
}
}
//
function createOffLineContext(bufferList){
console.log('createOfflineContext');
offline = new webkitOfflineAudioContext(2, window.voiceBuffer.length, 44100);
var vocalSource = offline.createBufferSource();
vocalSource.buffer = window.voiceBuffer;
vocalSource.connect(offline.destination);
var backing = offline.createBufferSource();
backing.buffer = bufferList[0];
backing.connect(offline.destination);
vocalSource.start(0);
backing.start(0);
offline.oncomplete = function(ev){
bufferList = null;
console.log('audioContext');
console.log(audioContext);
audioContext = null;
console.log(audioContext);
vocalSource.stop(0);
backing.stop(0);
vocalSource.disconnect(0);
backing.disconnect(0);
vocalSource = null;
backing = null;
window.voiceBuffer = null;
window.renderedFile = ev.renderedBuffer;
var bufferR = ev.renderedBuffer.getChannelData(0);
var bufferL = ev.renderedBuffer.getChannelData(1);
var interleaved = interleave(bufferL, bufferR);
var dataview = encodeWAV(interleaved);
window.audioBlob = new Blob([dataview], {type: 'Wav'});
saveFile();
}
offline.startRendering();
}
// This file is very long, but once it is finished mixing the two audio buffers it writes a new file to the file system. And when that operation is complete I use
function gotFileWriter(writer){
console.log('gotFileWriter');
writer.onwriteend = function(evt){
console.log('onwriteEnd');
console.log(window.audioBlob);
delete window.audioBlob;
console.log(window.audioBlob);
// checkDirectory();
var ref = window.open('index.html', '_self');
// ref.addEventListener('exit', windowClose);
}
writer.write(audioBlob);
}
I return back to the original index.html file. This solves the memory issues. However, once I try to run the same operation a second time. ie load in the record.html file, and run the recordLoad.js file I receive an error ReferenceError: Can't find variable: LocalFileSystem
It would appear that in reload index.html some, but not all of the links to the Cordova API have been lost. I can still for example use the Media Api but not the File Api. I understand that this is a bit of a hacky way, (opening and closing windows) to solve the memory leak, but I cannot find any other way of doing it. I really need some help with this. So any pointers very very welcome.
Folks,
I'm using Jquery UI - Tab.
I've an edit screen where the main form and tabs have been shown below. Now when I navigate from one record to another the Ajax call goes to the server to fetch new main record.
Now I want to refresh the tab below, with new record id, as well so what I've done is the following:
var jsonTabMetaData = [{"HtmlName":"Notes","Text":"Notes","Url":"../Notes.rl?moduleName=glbModuleName&moduleRecordID=glbModuleRecordID&sessionID=glbSessionID&company=glbCompanyName","Selected":false,"ModuleName":null,"ModuleRecordID":0},{"HtmlName":"AddressTel","Text":"Address & Telephone","Url":"../PhysicalAddress.rl/QuickAddress?moduleName=glbModuleName&moduleRecordID=glbModuleRecordID&sessionID=glbSessionID&company=glbCompanyName","Selected":false,"ModuleName":null,"ModuleRecordID":0},{"HtmlName":"Sendout","Text":"Send outs","Url":"../Sendouts.rl/List?moduleName=glbModuleName&moduleRecordID=glbModuleRecordID","Selected":false,"ModuleName":null,"ModuleRecordID":0},
function fnReboundTabs() {
$('#tabs a').each(function (index) {
var newUrl = jsonTabMetaData[$(this).attr("data-index")].Url;
newUrl = newUrl.replace("glbModuleRecordID", glbModuleRecordID);
newUrl = newUrl.replace("glbModuleName", glbModuleName);
newUrl = newUrl.replace("glbSessionID", glbSessionID);
newUrl = newUrl.replace("glbCompanyName", glbCompanyName);
this.href = newUrl;
});
`
if (firstTimeReboundTabs) {
firstTimeReboundTabs = false;
$("#tabs").tabs({
select: function (event, ui) {
},
cache: true,
event: '<%= (UI.Web.Helper.SessionMaster.OpenTabOnMouseOver) ? "mouseover": "click" %>',
async: false,
ajaxOptions: {
cache: false,
success: function () { },
error: function (xhr, status, index, anchor) {
$(anchor.hash).html(
"Couldn't load this tab. Should you see this error again, please notify admin.");
}
}
});
}
`
Now the problem is this:
When I navigate, the value changes in the URL, but the tab click request is opening into the new screen.
I.e. It is not working as Ajax call.
The Main screen goes and the URL open as a new URL in the browser address bar.
If I'm following this correctly, you want to change the URL of a tab's content when recordId is changed, and reload that tab without reloading the entire page.
This is possible using two methods of your tabs object:
To change the url, use the .tabs("url", index, url) where:
index = the index of the tab you are updating
url = the string of the new URL
To reload the tab's content at any time use .tabs("load", index)
index = the index of the tab you are updating.
Using these together should do what you want. I.e. when you have a new recordId do:
mytabs.tabs("url", i, 'mypage?recordId' + newRecordId)
mytabs.tabs("load", i)
The documentation is here, under the 'methods' tab: jqueryui docs
This is what I've done now:
function fnReboundTabs() {
for (var idx = 0; idx < jsonTabMetaData.length; idx++) {
var newUrl = jsonTabMetaData[idx].Url;
newUrl = newUrl.replace("glbModuleRecordID", glbModuleRecordID);
newUrl = newUrl.replace("glbModuleName", glbModuleName);
newUrl = newUrl.replace("glbSessionID", glbSessionID);
newUrl = newUrl.replace("glbCompanyName", glbCompanyName);
$("#tabs").tabs("url", idx, newUrl)
}
if (isNaN($('#tabs').tabs().tabs('option', 'selected')))
{ }
else {
$("#tabs").tabs("load", $('#tabs').tabs().tabs('option', 'selected'))
}
}
This function will then be called by when the main record has been downloaded at client side - JSON/ AJAX based.