User selects image from his pc. Then app reads file with FileReader as DataUrl and then the result is dispatched in store. And now I need to make an image for display from that DataUrl. I think it should be somehow transferred and parsed in react-konva.
inputImageChanged = (e: React.ChangeEvent<HTMLInputElement>) => {
const file = e.currentTarget.files[0];
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = evt =>
this.props.dispatch(
surfaceGridModalActions.inputSurfaceGridImage(evt.target.result)
);
};
You just need to use that data url as image source:
class UserImage extends React.Component {
state = {
image: new window.Image()
};
componentDidMount() {
this.state.image.src = this.props.dataURL;
this.state.image.onload = () => {
// so we need to update layer manually
this.imageNode.getLayer().batchDraw();
};
}
render() {
return (
<Image
image={this.state.image}
y={250}
ref={node => {
this.imageNode = node;
}}
/>
);
}
}
Related
Below I try to respond with a stream when I receive ticker updates.
+page.server.js:
import YahooFinanceTicker from "yahoo-finance-ticker";
const ticker = new YahooFinanceTicker();
const tickerListener = await ticker.subscribe(["BTC-USD"])
const stream = new ReadableStream({
start(controller) {
tickerListener.on("ticker", (ticker) => {
console.log(ticker.price);
controller.enqueue(ticker.price);
});
}
});
export async function load() {
return response????
};
Note: The YahooFinanceTicker can't run in the browser.
How to handle / set the response in the Sveltekit load function.
To my knowledge, the load functions cannot be used for this as their responses are JS/JSON serialized. You can use an endpoint in +server to return a Response object which can be constructed from a ReadableStream.
Solution: H.B. comment showed me the right direction to push unsollicited price ticker updates the client.
api route: yahoo-finance-ticker +server.js
import YahooFinanceTicker from "yahoo-finance-ticker";
const ticker = new YahooFinanceTicker();
const tickerListener = await ticker.subscribe(["BTC-USD"])
/** #type {import('./$types').RequestHandler} */
export function GET({ request }) {
const ac = new AbortController();
console.log("GET api: yahoo-finance-ticker")
const stream = new ReadableStream({
start(controller) {
tickerListener.on("ticker", (ticker) => {
console.log(ticker.price);
controller.enqueue(String(ticker.price));
}, { signal: ac.signal });
},
cancel() {
console.log("cancel and abort");
ac.abort();
},
})
return new Response(stream, {
headers: {
'content-type': 'text/event-stream',
}
});
}
page route: +page.svelte
<script>
let result = "";
async function getStream() {
const response = await fetch("/api/yahoo-finance-ticker");
const reader = response.body.pipeThrough(new TextDecoderStream()).getReader();
while (true) {
const { value, done } = await reader.read();
console.log("resp", done, value);
if (done) break;
result += `${value}<br>`;
}
}
getStream();
</script>
<section>
<p>{#html result}</p>
</section>
I would like my uploadFormPage() function to be able to take jpegs and pdf's. Is it possible for me to have 2 file types for the same FormData() const?`
export function uploadFormPage(documentId, formId, file, callback) {
return async dispatch => {
try {
const formData = new FormData();
formData.append('page', {
name: `document-${documentId}-${formId}-${Date.now()}.jpg`,
type: 'image/jpeg',
uri: file,
});
const result = await Api.uploadFiles(formData);
const entity = {
id: formId,
resourceKey: result.page,
};
const rsp = await Api.uploadFormPage(documentId, entity);
dispatch({type: LOAD_DOCUMENTS, data: rsp});
callback(null, rsp);
} catch (e) {
callback(e, null);
}
};
}
I'm trying to upload a file using apollo-server-express and apollo-client. However, when the file object is passed to the resolver it is always empty. I can see the file on the client, but not the server side. How can I resolve this ?
My Schema
type File {
id: ID
path: String
filename: String
mimetype: String
}
extend type Query {
getFiles: [File]
}
extend type Mutation {
uploadSingleFile(file: Upload!): File
}
My Resolver
Mutation: {
uploadSingleFile: combineResolvers(
isAuthenticated,
async (parent, { file }, { models, user, storeUpload }, info) => {
console.log('Resolver-> uploadSingleFile')
console.log(file) // Will return empty, { }
const x = await file
console.log(x) // Will also return empty, { }
const storedFile = storeUpload(file)
return storedFile
}
),
},
My Client-side queries file
export const UPLOAD_SINGLE_FILE = gql`
mutation uploadSingleFile($file: Upload!) {
uploadSingleFile(file: $file) {
id
}
}
`
My Client-side interface
import React from 'react'
// GQL
import { useApolloClient, useMutation } from '#apollo/react-hooks'
import { UPLOAD_SINGLE_FILE } from '../../queries'
const FileUpload = props => {
const [uploadSingleFile, uploadSingleFileResult] = useMutation(UPLOAD_SINGLE_FILE, {
onCompleted(uploadSingleFile) {
console.log('Completed uploadSingleFile')
}
})
const apolloClient = useApolloClient()
const handleUploadFile = ({
target: {
validity,
files: [file]
}
}) => {
console.log('Uploading file...')
if(validity.valid) {
console.log('Valid')
console.log(file.name)
uploadSingleFile({ variables: { file } })
.then(() => {
apolloClient.resetStore()
})
}
else console.log('Invalid file')
}
return(
<input type="file" required onChange={handleUploadFile} />
)
}
export default FileUpload
UPDATED
My front-end set-up is:
const httpLink = createHttpLink({
uri: 'http://localhost:4000/graphql',
})
const authLink = setContext((_, { headers }) => {
const token = localStorage.getItem('token')
return {
headers: {
...headers,
authorization: token ? `Bearer ${token}` : "",
}
}
})
const client = new ApolloClient({
link: authLink.concat(httpLink),
cache: new InMemoryCache(),
})
You need to utilize the appropriate Link with your Apollo Client in order to enable file uploads. The easiest way to do that is by using createUploadLink from apollo-upload-client. It functions as a drop-in replacement for createHttpLink, so just swap out the functions and you'll be good to go.
const httpLink = createUploadLink({
uri: 'http://localhost:4000/graphql',
})
const authLink = ...
const client = new ApolloClient({
link: authLink.concat(httpLink),
cache: new InMemoryCache(),
})
Assuming you have the proper link set up and in use (using createUploadLink as Daniel mentions in his post), you should be able to destructure the props from file once you await the promise in your resolver on the server.
const { filename, mimetype, createReadStream } = await file.promise;
console.log(filename, mimetype);
// to get a stream to use of the data
const stream = createReadStream();
UPDATE: in more recent versions of graphql-upload you can just await the file like you do in the OP, rather than the file.promise. I was using an older version of the lib it seems.
I would like to know how to pick an Image from the users computer into my flutter web app for upload
Using dart:html package directly in Flutter is not recommended.
Instead, use this package: https://pub.dev/packages/file_picker.
Example of how to use in Flutter Web:
class FileUploadButton extends StatelessWidget {
#override
Widget build(BuildContext context) {
return RaisedButton(
child: Text('UPLOAD FILE'),
onPressed: () async {
var picked = await FilePicker.platform.pickFiles();
if (picked != null) {
print(picked.files.first.name);
}
},
);
}
}
Note that FilePickerResult.path is not supported in Flutter Web.
I tried the code below and it worked.
first import 'dart:html';
// variable to hold image to be displayed
Uint8List uploadedImage;
//method to load image and update `uploadedImage`
_startFilePicker() async {
InputElement uploadInput = FileUploadInputElement();
uploadInput.click();
uploadInput.onChange.listen((e) {
// read file content as dataURL
final files = uploadInput.files;
if (files.length == 1) {
final file = files[0];
FileReader reader = FileReader();
reader.onLoadEnd.listen((e) {
setState(() {
uploadedImage = reader.result;
});
});
reader.onError.listen((fileEvent) {
setState(() {
option1Text = "Some Error occured while reading the file";
});
});
reader.readAsArrayBuffer(file);
}
});
}
now just any Widget, like a button and call the method _startFilePicker()
import 'package:http/http.dart' as http;
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
class FileUploadWithHttp extends StatefulWidget {
#override
_FileUploadWithHttpState createState() => _FileUploadWithHttpState();
}
class _FileUploadWithHttpState extends State<FileUploadWithHttp> {
PlatformFile objFile = null;
void chooseFileUsingFilePicker() async {
//-----pick file by file picker,
var result = await FilePicker.platform.pickFiles(
withReadStream:
true, // this will return PlatformFile object with read stream
);
if (result != null) {
setState(() {
objFile = result.files.single;
});
}
}
void uploadSelectedFile() async {
//---Create http package multipart request object
final request = http.MultipartRequest(
"POST",
Uri.parse("Your API URL"),
);
//-----add other fields if needed
request.fields["id"] = "abc";
//-----add selected file with request
request.files.add(new http.MultipartFile(
"Your parameter name on server side", objFile.readStream, objFile.size,
filename: objFile.name));
//-------Send request
var resp = await request.send();
//------Read response
String result = await resp.stream.bytesToString();
//-------Your response
print(result);
}
#override
Widget build(BuildContext context) {
return Container(
child: Column(
children: [
//------Button to choose file using file picker plugin
RaisedButton(
child: Text("Choose File"),
onPressed: () => chooseFileUsingFilePicker()),
//------Show file name when file is selected
if (objFile != null) Text("File name : ${objFile.name}"),
//------Show file size when file is selected
if (objFile != null) Text("File size : ${objFile.size} bytes"),
//------Show upload utton when file is selected
RaisedButton(
child: Text("Upload"), onPressed: () => uploadSelectedFile()),
],
),
);
}
}
I've tested this package and was very happy with the result imagePickerWeb it returns 3 different types it can be in the form of Image(widget for preview), byte, File(upload)
then you can use this to get the values
html.File _cloudFile;
var _fileBytes;
Image _imageWidget;
Future<void> getMultipleImageInfos() async {
var mediaData = await ImagePickerWeb.getImageInfo;
String mimeType = mime(Path.basename(mediaData.fileName));
html.File mediaFile =
new html.File(mediaData.data, mediaData.fileName, {'type': mimeType});
if (mediaFile != null) {
setState(() {
_cloudFile = mediaFile;
_fileBytes = mediaData.data;
_imageWidget = Image.memory(mediaData.data);
});
}
Uploading to firebase
don't forget to add this to your index.html
<script src="https://www.gstatic.com/firebasejs/7.5.0/firebase-storage.js"></script>
Uploading to firebase
import 'package:firebase/firebase.dart' as fb;
uploadToFirebase(File file) async {
final filePath = 'temp/${DateTime.now()}.png';//path to save Storage
try {
fb
.storage()
.refFromURL('urlFromStorage')
.child(filePath)
.put(file);
} catch (e) {
print('error:$e');
}
}
See the documentation of the package if you still have problems
The accepted answer is indeed outdated. Like jnt suggested, https://pub.dev/packages/file_picker is a handy package, when it comes to implementing an image upload using Flutter Web.
The problem I was facing is to get a base64 representation of an image, since I was using it to store images in Firestore. As we know, dart:io is not supported on Flutter Web and throws Unsupported operation: _Namespace error. Hence, using File and reading file's bytes was not an option. Luckily, the package provides API to convert the uploaded image to Uint8List. Here is my implementation:
import 'package:file_picker/file_picker.dart';
...
FilePickerResult? pickedFile;
...
void chooseImage() async {
pickedFile = await FilePicker.platform.pickFiles();
if (pickedFile != null) {
try {
setState(() {
logoBase64 = pickedFile!.files.first.bytes;
});
} catch (err) {
print(err);
}
} else {
print('No Image Selected');
}
}
In case you need to display the local image right away, use Image.memory.
Image.memory(logoBase64!);
i have this problem too;
i use https://pub.dev/packages/file_picker but in flutter web path not suppor;
you should to use bytes;
i save file bytes in var _fileBytes and use in request;
var request = http.MultipartRequest('POST', Uri.parse('https://.....com'));
request.headers.addAll(headers);
request.files.add(
http.MultipartFile.fromBytes(
'image',
await ConvertFileToCast(_fileBytes),
filename: fileName,
contentType: MediaType('*', '*')
)
);
request.fields.addAll(fields);
var response = await request.send();
function ConvertFileToCast:
ConvertFileToCast(data){
List<int> list = data.cast();
return list;
}
it`s work for me :)
if anyone is wondering how to get it working on mobile and web :
var bytes;
await file!.files.first.readStream!
.map((asciiValue) => bytes = asciiValue)
.toList();
FormData body;
final MultipartFile file = MultipartFile.fromBytes(bytes, filename: "file");
MapEntry<String, MultipartFile> imageEntry = MapEntry("image", file);
body.files.add(imageEntry);
I can share the way I upload image to AWS s3 from flutter web recently.
May not exact match the case who is looking for answer here but I think it may inpired others somehow.
First I try to use amplify_storage_s3 package but it not support for Flutter Web yet for now. So I use basic http post instead.
The packages I use:
file_picker: For web, FileUploadInputElement (from html package) may do the same thing but I think using this package can make thing simpler.
dio: I'm not sure why I cannot use http's MultipartFile successfully so I use this instead. (maybe someone can provide a version using http package)
mine: transfer extension to mimetype
Code example:
import 'package:flutter/material.dart';
import 'package:dio/dio.dart' as dio;
import 'package:file_picker/file_picker.dart';
import 'package:mime/mime.dart';
class FileUploader extends StatelessWidget {
const FileUploader({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: FloatingActionButton(
onPressed: () async {
// 1. Pick an image file
final filePicked = await FilePicker.platform.pickFiles();
if (filePicked != null) {
final file = filePicked.files.single; // PlatformFile
final mimeType = lookupMimeType(file.name) ?? '';
/// 2. Get presigned data somewhere
const url = 'https://s3.amazonaws.com/......';
final fields = {
'bucket': '...',
'X-Amz-Algorithm': 'AWS4-HMAC-SHA256',
'X-Amz-Credential': '...',
'X-Amz-Date': '...',
'Policy': '...',
'X-Amz-Signature': '...',
'x-amz-meta-userid': '...',
'Content-Type': mimeType,
'file': dio.MultipartFile.fromBytes(file.bytes ?? []),
};
/// 3. Send file to AWS s3
final formData = dio.FormData.fromMap(fields);
await dio.Dio().post(url, data: formData);
}
},
child: const Icon(Icons.upload),
),
);
}
}
Here is my working code to upload using dio. I use dio because it has a callback progress function.
class _FileUploadViewState extends State<FileUploadView> {
#override
void initState() {
super.initState();
}
FilePickerResult? result;
PlatformFile? file;
Response? response;
String? progress;
String? percentage;
Dio dio = Dio();
selectFile() async {
result =
await FilePicker.platform.pickFiles(type: FileType.any, withData: true);
if (result != null) {
file = result?.files.single;
}
//print(file?.name);
//print(file?.bytes?.length);
//print(file?.size);
//print(file?.extension);
//print(file?.path);
setState(() {});
}
Future<void> uploadFile(BuildContext context, User user) async {
final navigator = Navigator.of(context);
final storage = FlutterSecureStorage();
String? token = await storage.read(key: 'jwt');
final formData = FormData.fromMap(
{
'file': MultipartFile.fromBytes(file?.bytes as List<int>,
filename: file?.name)
},
);
dio.options.headers['content-Type'] = 'application/octet-stream';
dio.options.headers["authorization"] = "Bearer $token";
response = await dio.post(
user.fileUrl,
data: formData,
onSendProgress: (int sent, int total) {
percentage = (sent / total * 100).toStringAsFixed(2);
progress = "$sent Bytes of $total Bytes - $percentage % uploaded";
setState(
() {},
);
},
);
if (response!.statusCode == 200) {
....
My go code for the server looks like this,
if err := r.ParseMultipartForm(64 << 20); err != nil {
log.Println("error processing multipart form")
log.Println(err)
http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
return
}
file, handler, err := r.FormFile("file")
I'm trying to record voice note on ios device using ionic cordova Media and File plugin and pushing it to firebase storage.
On android is working well.
This is my code:
First I created the init() function
init(): Promise < any > {
this.date = moment().format('x');
return new Promise((resolve, reject) => {
let currentFile: File;
this.fileName = this.date + `-rnb.mp3`;
this.file.createFile(this.platform.is('ios') ? cordova.file.tempDirectory : cordova.file.dataDirectory, this.fileName, true).then((result) => {
this.current_file_playing = this.createAudioFile(this.storageDirectory, this.fileName);
resolve();
}, (e) => {
console.log(JSON.stringify(e, null, 2));
reject(e);
})
});
}
this.storageDirectory it's a variable defined in the provider constructor() equal to directory path depends on the platform. and this is the following code:
this.platform.ready().then(() => {
if (this.platform.is('ios')) {
this.storageDirectory = this.file.tempDirectory;
} else if (this.platform.is('android')) {
this.storageDirectory = this.file.externalDataDirectory;
}
});
Then the record() function is listener to record button
record(){
return new Promise((resolve,reject)=>{
this.init().then((media:Media) => {
try {
this.startRecording(media);
resolve(media);
} catch (e) {
console.log(e);
}
});
});
}
This is the startRecording() function:
startRecording(mediaPlugin: any) {
this.current_file_playing.startRecord();
}
Moreover stopRecording() function is a listener to stop button:
stopRecording(mediaPlugin: any) {
return new Promise((resolve,reject)=>{
this.current_file_playing.stopRecord();
this.current_file_playing.play();
this.current_file_playing.setVolume(0.0); //trick
this.saveFirebase().then((downloadUrl) => {
resolve(downloadUrl);
});
});
}
And Finally this is how I'm pushing to firebase, using saveFirebase() function
saveFirebase() {
return new Promise((resolve, reject) => {
let storageRef = firebase.storage().ref();
let metadata = {
contentType: 'audio/mp3',
};
this.file.readAsDataURL(this.storageDirectory, this.fileName).then((file) => {
let voiceRef = storageRef.child(`voices/${this.fileName}`).putString(file, firebase.storage.StringFormat.DATA_URL);
voiceRef.on(firebase.storage.TaskEvent.STATE_CHANGED, (snapshot) => {
console.log("uploading");
}, (e) => {
console.log('inside the error');
reject(e);
console.log(JSON.stringify(e, null, 2),'this.error');
}, () => {
var downloadURL = voiceRef.snapshot.downloadURL;
resolve(downloadURL);
});
});
});
}
Explanation of saveFirebase() function
First I transformed the file to base64 using this.file.readAsDataURL(...) then I pushed the Firebase Storage using putString method.
The audio file is successfully pushed to Firebase Storage, But with 0 Byte size. That is mean to pushing to Firebase is working well, but the recording voice to the file is not working.
The audio files that have size is recorded from android device.
Anyone have an idea what is my problem?
Thanks.
The problem was:
The audio file should be .wav. So when I changed the type of audio to became wav.
let metadata = {
contentType: 'audio/wav',
};
this.fileName = this.date + `-rnb.mp3`;
It work for me.
Thanks.