I am trying to implement zip file upload functionality in Angular 8 app. 3 conditions that I need to satisfy are:
1. Only allow zip files to be uploaded else throw error message
2. File size should not cross 3 MBs else throw error message
3. When I choose zip file, it should show progress bar but file should only be uploaded via REST API call when I click 'Register' button separately.
What I have implemented so far is:File Upload Service
postFile(fileToUpload: File, header): Observable<any> {
const endpoint = 'your-destination-url';
const formData: FormData = new FormData();
formData.append('fileKey', fileToUpload, fileToUpload.name);
if (fileToUpload.size <= 3048576)
return this.httpClient.post(endpoint, formData, { headers: header })
.pipe(map(data => {
console.log(data);
return data;
},error => {
console.log(error, 'reduce file size');
}))
}
Component TS File
handleFileInput(files: FileList) {
this.fileToUpload = files.item(0);
}
uploadFileToActivity() {
this.fileUploadService.postFile(this.fileToUpload, this.headers).subscribe(data => {
// do something, if upload success
console.log('the file has been uploaded successfully', data);
}, error => {
console.log(error);
});
}
Component HTML
<input type="file"
id="file" (change)="handleFileInput($event.target.files)">
Please suggest how can I modify so that my functionality is as described.
for points 1 and 2 you should add a validation function in your code to check both the file extension and the size.
The upload should be possible only if the file passes the validation.
In addition to that, you should probably return some kind of feedback to the user when the validation fails.
You can track the file upload progress (and show a progress bar) adding additional options to the .post method and listening for specific events
return this.httpClient.post(endpoint, formData, {
headers: header,
reportProgress: true,
observe: 'events'
}).pipe(map(event => {
if (event.type === HttpEventType.Response) {
// upload complete
}
if (event.type === HttpEventType.UploadProgress) {
// the event contains information about loaded data
// you can use event.loaded and event.total to display the progress bar
}
}))
Related
In my application user can upload image from his/her mobile device. Since i am facing problem in converting HEIC image to jpg.
Application create its thumbnails too.
While searching on google i found "https://github.com/alexcorvi/heic2any". which helped me a little. But here i have to enter the physical path of the like -> fetch('path/to/image.heic') this. But in angular we handle file upload like -> event.target.files[0] on file upload.
fetch('path/to/image.heic')
.then((res) => res.blob())
.then((blob) => heic2any({ blob }))
.then((conversionResult) => {
console.log(conversionResult);
})
.catch((e) => {
console.log(e);
});
Since i am implementing this in angular, How i am reading the file object is like this
handle(event) {
if (event.target.files && event.target.files[0]) {
var reader = new FileReader();
reader.onload = (event: any) => {
console.log(event);
}
console.log(reader.readAsDataURL(event.target.files[0]));
}
}
How to proceed so that i can save image in jpg format in my backend(laravel 5.2).
Thanks
I'm currently unsuccessfully trying to make my PWA installable. I have registered a SertviceWorker and linked a manifest as well as I am listening on the beforeInstallPromt event.
My ServiceWorker is listening to any fetch event.
My problem is, that the created beforeInstall banner is just being shown on Chrome desktop but on mobile I get a warning in Chrome inspection tab "Application" in the "Manifest" section:
Installability
Service worker does not have the 'fetch' handler
You can check the message on https://dev.testapp.ga/
window.addEventListener('beforeinstallprompt', (e) => {
// Stash the event so it can be triggered later.
deferredPrompt = e;
mtShowInstallButton();
});
manifest.json
{"name":"TestApp","short_name":"TestApp","start_url":"https://testapp.ga/loginCheck","icons":[{"src":"https://testapp.ga/assets/icons/launcher-ldpi.png","sizes":"36x36","density":0.75},{"src":"https://testapp.ga/assets/icons/launcher-mdpi.png","sizes":"48x48","density":1},{"src":"https://testapp.ga/assets/icons/launcher-hdpi.png","sizes":"72x72","density":1.5},{"src":"https://testapp.ga/assets/icons/launcher-xhdpi.png","sizes":"96x96","density":2},{"src":"https://testapp.ga/assets/icons/launcher-xxhdpi.png","sizes":"144x144","density":3},{"src":"https://testapp.ga/assets/icons/launcher-xxxhdpi.png","sizes":"192x192","density":4},{"src":"https://testapp.ga/assets/icons/launcher-web.png","sizes":"512x512","density":10}],"display":"standalone","background_color":"#ffffff","theme_color":"#0288d1","orientation":"any"}
ServiceWorker:
//This array should NEVER contain any file which doesn't exist. Otherwise no single file can be cached.
var preCache=[
'/favicon.png',
'/favicon.ico',
'/assets/Bears/bear-standard.png',
'/assets/jsInclude/mathjax.js',
'/material.js',
'/main.js',
'functions.js',
'/material.css',
'/materialcolors.css',
'/user.css',
'/translations.json',
'/roboto.css',
'/sw.js',
'/'
];
//Please specify the version off your App. For every new version, any files are being refreched.
var appVersion="v0.2.1";
//Please specify all files which sould never be cached
var noCache=[
'/api/'
];
//On installation of app, all files from preCache are being stored automatically.
self.addEventListener('install', function(event) {
event.waitUntil(
caches.open(appVersion+'-offline').then(function(cache) {
return cache.addAll(preCache).then(function(){
console.log('mtSW: Given files were successfully pre-cached')
});
})
);
});
function shouldCache(url) {
//Checking if url is market as noCache
var isNoCache=noCache.includes(url.substr(8).substr(url.substr(8).indexOf("/")))||noCache.includes((url.substr(8).substr(url.substr(8).indexOf("/"))).substr(0,(url.substr(8).substr(url.substr(8).indexOf("/"))).indexOf("?")));
//Checking of hostname of request != current hostname
var isOtherHost=url.substr(8).substr(0,url.substr(8).indexOf("/"))!=location.hostname&&url.substr(7).substr(0,url.substr(7).indexOf("/"))!=location.hostname;
return((url.substr(0,4)=="http"||url.substr(0,3)=="ftp") && isNoCache==false && isOtherHost==false);
}
//If any fetch fails, it will look for the request in the cache and serve it from there first
self.addEventListener('fetch', function(event) {
//Trying to answer with "online" version if fails, using cache.
event.respondWith(
fetch(event.request).then(function (response) {
if(shouldCache(response.url)) {
console.log('mtSW: Adding file to cache: '+response.url);
caches.open(appVersion+'-offline').then(function(cache) {
cache.add(new Request(response.url));
});
}
return(response);
}).catch(function(error) {
console.log( 'mtSW: Error fetching. Serving content from cache: ' + error );
//Check to see if you have it in the cache
//Return response
//If not in the cache, then return error page
return caches.open(appVersion+'-offline').then(function (cache) {
return cache.match(event.request).then(function (matching) {
var report = !matching || matching.status == 404?Promise.reject('no-match'): matching;
return report
});
});
})
);
})
I checked the mtShowInstallButton function. It's fully working on desktop.
What does this mean? On the Desktop, I never got this warning, just when using a handheld device/emulator.
Fetch function is used to fetch JSon manifest file. Try reading google docs again.
For adding PWA in Mobile you need manifest file to be fetched which is fetched using service-worker using fetch function.
Here is the code :
fetch('examples/example.json')
.then(function(response) {
// Do stuff with the response
})
.catch(function(error) {
console.log('Looks like there was a problem: \n', error);
});
for more about fetch and manifest try this.
I am trying to upload files from Angular 4 app to a JSON API service that accepts base64 strings as file content.
So what I do is - read the file with FileReader.readAsDataURL, then when user confirms the upload I will create a JSON request to the API and send the base64 string of the file I got earlier.
This is where the problem starts - as soon as I do something with the "content" (log it, send it, w/e) the request will be send, but its insanely slow, e.g. 20 seconds for 2MB file.
I have tried:
using ArrayBuffer and manually converting it to base64
storing the base64 string in HTML and retrieving it later
reading the files after user clicks on upload button
using the old client from #angular/common
using plain XHR request
but everything leads to the same result.
I know where the problem lies. But why does it happen? Is it something browser specific or angular specific? Is there a more preferred approach (keep in mind it has to be base64 string)?
Notes:
changing anything in the API is beyond my control
API is fine, sending any file trough postman will finish immediately
Code:
This method runs when user adds file to the dropzone:
public onFileChange(files: File[]) : void {
files.forEach((file: File, index: number) => {
const reader = new FileReader;
// UploadedFile is just a simple model that contains filename, size, type and later base64 content
this.uploadedFiles[index] = new UploadedFile(file);
//region reader.onprogress
reader.onprogress = (event: ProgressEvent) => {
if (event.lengthComputable) {
this.uploadedFiles[index].updateProgress(
Math.round((event.loaded * 100) / event.total)
);
}
};
//endregion
//region reader.onloadend
reader.onloadend = (event: ProgressEvent) => {
const target: FileReader = <FileReader>event.target;
const content = target.result.split(',')[1];
this.uploadedFiles[index].contentLoaded(content);
};
//endregion
reader.readAsDataURL(file);
});
}
This method runs when users clicks save button
public upload(uploadedFiles: UploadedFile[]) : Observable<null> {
const body: object = {
files: uploadedFiles.map((uploadedFile) => {
return {
filename: uploadedFile.name,
// SLOWDOWN HAPPENS HERE
content: uploadedFile.content
};
})
};
return this.http.post('file', body)
}
For sending big files to server you should use FormData to be able to send it as multi-part instead of a single big file.
Something like:
// import {Http, RequestOptions} from '#angular/http';
uploadFileToUrl(files, uploadUrl): Promise<any> {
// Note that setting a content-type header
// for mutlipart forms breaks some built in
// request parsers like multer in express.
const options = new RequestOptions();
const formData = new FormData();
// Append files to the virtual form.
for (const file of files) {
formData.append(file.name, file)
}
// Send it.
return this.http.post(uploadUrl, formData, options);
}
Also don't forget to set the header 'Content-Type': undefined, I've scratched my head over this for hours.
I am working on a project using meteor and collectionfs.
I upload files to collectionfs and have a filehandler in place. I can use the
{{cfsFileUrl "defaultFilehandler"}}
Handlebar Helper to display the url where the image is saved, but I can't download images from this URL.
When I copy it into my browser:
localhost:3000/cfs/contacts/Nj3WzrBKhqd9Mc9NP_defaultHandler.png
meteor routes me to the meteor page ( as if i had writen localhost:3000 )
Ultimately I would like to achieve two things:
1st
display the image using an html tag:
<img src=??? alt="your image" />
2nd
I would like to make sure that the user is allowed to see this image.
Having the 'download-url' is not sufficient security for me.
In order to get to the point i went through the normal tutorial from collectionFS:
client js
ContactsFS = new CollectionFS('contacts', { autopublish: false });
Deps.autorun(function() {
Meteor.subscribe('myContactsFiles');
});
Template.queueControl.events({
'change .fileUploader': function (e) {
var files = e.target.files;
for (var i = 0, f; f = files[i]; i++) {
ContactsFS.storeFile(f);
}
}
});
server js
ContactsFS = new CollectionFS('contacts', { autopublish: false });
ContactsFS.allow({
insert: function(userId, file) {
console.log('user'+userId+"file"+JSON.stringify(file));
console.log("WILL SAVE:"+userId && file.owner === userId );
return userId && file.owner === userId;
},
update: function(userId, files, fields, modifier) {
return _.all(files, function (file) {
return (userId == file.owner);
}); //EO iterate through files
},
remove: function(userId, files) { return false; }
});
Meteor.publish('myContactsFiles', function() {
if (this.userId) {
return ContactsFS.find({ owner: this.userId }, { limit: 30 });
}
});
ContactsFS.fileHandlers({
default1: function(options) { // Options contains blob and fileRecord — same is expected in return if should be saved on filesytem, can be modified
return { blob: options.blob, fileRecord: options.fileRecord }; // if no blob then save result in fileHandle (added createdAt)
}});
Answer 1:
You can use the updated 0.3.3+ version of collectionFS to publicly make those files accessable, or view them.
<img src="{{cfsFileUrl 'default1'}}">
where default1 is the name of the handler function you defined in ContactsFS.fileHandlers
Answer 2:
There is no secure solution build into collectionFS as of now, its in the making tho.
I am trying to save attachments in ravenDb. I am getting a file not found error.
MVC View:
<input type="file" name="file" id="Ids2" style="float:right"/>
Over an ajax call, I am passing the value of the file name selected in the above control to the controller method - which in turns sends the file name to a custom method called "Upload"
public virtual string Upload(string fileName)
{
IDocumentSession session = GetCurrentDocumentSession();
var id = "upload/" + randomGen();
session.Advanced.DatabaseCommands.PutAttachment(id,null,
File.ReadAllBytes(fileName), optionalMetaData);
return id;
}
I am getting C:\ProgramFiles (x86)....does not have the file specified.
Lets say in the view - I browsed to C:/Doc1.txt and clicked on Add button that saves bunch of other fields on the view and also picks up the file name/path from the file upload control.
I get an error at session.advance.databasecommands... line
Could not find file 'C:\Program Files (x86)\Common Files\Microsoft Shared\DevServer\10.0\Doc1.txt'.
If I manually move the Doc1.txt file to the above location, ravenDB saves the attachment and I can see it from localhost:8080/static/upload/keyvalue
How can I make ravenDB take the file from the location the user selects and not from the what it looks like a default location of c:programfiles.....
EDIT:
function () {
var iFile = iContainer.find( '#Ids2' ).val();
var DataToSave = {
'Attachment' : iFile
};
var encodedData = $.toJSON(DataToSave);
$.ajax({
type: 'POST' ,
url: '/AttController/Attach' ,
data: encodedData,
contentType: 'application/json; charset=utf-8' ,
success: function (rc) {
if (rc.Success) {
// more javascript reroutes..business logic
}
else {
alert(rc.Message);
}
},
error: function (xhr, ajaxOptions, thrownError) {
alert( 'Error attaching \n' + xhr.response);
}
});
};
Depending on the browser The html file control does not store the full path to the file. If you use Chrome and debug the script
var iFile = iContainer.find( '#Ids2' ).val();
Will return something like C:\fakepath\yourfile.txt. where as with IE the full path is returned.
Also you in your Ajax you are not pushing the bytes of the file but only the filename which means unless you are going to only ever run this website in a browser on the webserver the chances of the file being in the same place as the webserver is slim.
If you are trying to upload a file via ajax to a MVC controller I would suggest uploadify.
$("#Ids2").uploadify(
{
uploader: '/AttController/Attach',
swf: 'your/path/to/uploadify.swf',
cancelImg: 'your/path/to/cancel.jpg',
buttonText: 'Select File',
fileSizeLimit: '300KB',
fileTypeDesc: 'Image Files',
fileTypeExts: '*.gif; *.jpg; *.png',
auto: 'true',
multiple: 'false',
onError: function(type, info) {
},
onUploadSuccess: function(file, data, response) {
}
});
Then just change your controller action to
public virtual ActionResult Upload(HttpPostedFileBase FileData)
The FileData would have things like the FileName and would also have the file in an input stream.