Dart is replacing "&" with "\u0026" in a URL - flutter

I am using flutter to process a link and download it to the device using dio package.
But the problem is dart is replacing all '&' with '\u0026' and making the link unusable. is there a way to avoid this problem? Thanks in advance.
Here's the code:
const uuid = Uuid();
final Dio dio = Dio();
// * create random id for naming downloaded file
final String randid = uuid.v4();
// * create a local instance of state all media
List<MediaModel> allMedia = state.allMedia;
// * create an instance of IGDownloader utility class from ~/lib/utilities
final IGDownloader igd = IGDownloader();
// * make a download link from provided link from the GetNewMedia event
final link = await igd.getPost(event.link);
link.replaceAll('\u0026', '&');
print(await link);
Output:
// expected : "http://www.example.com/example&examples/"
// result: "http://www.example.com/example\u0026example"

Pass your url to cleanContent function and don't forget to add imports
import 'package:html/parser.dart';
import 'package:html_unescape/html_unescape.dart';
static String cleanContent(String content, {bool decodeComponent = false}) {
if (content.contains("<p>")) {
content = content.replaceAll("<p>", "").trim();
}
if (content.contains("</p>")) {
content = content.replaceAll("</p>", "").trim();
}
var unescape = HtmlUnescape();
content = unescape.convert(content).toString();
if (content.contains("\\<.*?\\>")) {
content = content.replaceAll("\\<.*?\\>", "").trim();
}
content = parseHtmlString(content,decodeComponent: decodeComponent);
return content;
}
static String parseHtmlString(String htmlString,{bool decodeComponent = false}) {
final document = parse(htmlString);
String parsedString = parse(document.body!.text).documentElement!.text;
if(parsedString.contains("%3A")){
parsedString = parsedString.replaceAll("%3A", ":");
}
if(parsedString.contains("%2F")){
parsedString = parsedString.replaceAll("%2F", "/");
}
if(decodeComponent){
parsedString = Uri.decodeComponent(parsedString);
}
return parsedString;
}

replaceAll returns the modified string, but leaves original String untouched.
Try:
print(await link.replaceAll('\u0026', '&'));
or
newLink = link.replaceAll('\u0026', '&');
print(await newLink);

Related

How to solve Error: XMLHttpRequest error. in Flutter Web

I want to use to Google places api and
I am trying to call this api but I am getting this. error
Error: XMLHttpRequest error.
static Future<List<Result>?> searchPlaces(context, String query) async {
String mapApiKey = "API_KEY";
String _host = 'https://maps.googleapis.com/maps/api/place/textsearch/json';
final url = '$_host?query=$query&key=$mapApiKey';
//
var response = await http.get(Uri.parse(url));
print(response.body);
//
if (response.statusCode == '200') {
GPlacesSearch result = GPlacesSearch.fromJson(jsonDecode(response.body));
return result.results!;
} else
return null;
}
}
I don't know which platform you are using, but I guess the solution would be to disable chrome web security.
If you are working on mac try the following steps
Go to flutter\bin\cache and remove a file named: flutter_tools.stamp
Go to flutter\packages\flutter_tools\lib\src\web and open the file chrome.dart.
Find '--disable-extensions'
Add '--disable-web-security'
And if you are working on windows just search for how to disable web security for chrome
Use this url 'https://cors-anywhere.herokuapp.com' before your actual url e.g.
String baseUrl = 'https://cors-anywhere.herokuapp.com';
String actualUrl = 'https://maps.googleapis.com/maps/api/place/textsearch/json';
String finalUrl = "$baseUrl/$actualUrl";
static Future<List<Result>?> searchPlaces(context, String query) async {
String mapApiKey = "YOUR_KEY_HERE";
var _sessionToken = Uuid().v4();
String _host =
'https://cors-anywhere.herokuapp.com/https://maps.googleapis.com/maps/api/place/textsearch/json';
final url = '$_host?query=$query&key=$mapApiKey';
//
var response = await http.get(Uri.parse(url);
//
GPlacesSearch result = GPlacesSearch.fromJson(jsonDecode(response.body));
return result.results!;
}
}

Flutter get all images from website with given URL

I am trying to scrape any website for its images and save them in a list. For that I am using the getElementsByTagname("img") and also selected the ['src'] attributes like this:
void _getData() async {
final response = await http.get(Uri.parse(_currentUrl));
final host = Uri.parse(_currentUrl).host;
dom.Document document = parser.parse(response.body);
final elements = document.getElementsByTagName("img").toList();
for (var element in elements) {
var imageSource = element.attributes['src'] ?? '';
print(imageSource);
bool validURL = Uri.parse(imageSource).host == '' ||
Uri.parse(host + imageSource).host == ''
? false
: true;
if (validURL && !imageSource.endsWith('svg')) {
Uri imageSourceUrl = Uri.parse(imageSource);
if (imageSourceUrl.host.isEmpty) {
imageSource = host + imageSource;
}
if (_imagesWithSize.firstWhereOrNull(
(element) => element.imageUrl == imageSource,
) ==
null) {
Size size = await _calculateImageDimension(imageSource);
_imagesWithSize.add(
ImageWithSize(
imageSource,
size,
),
);
}
}
}
_imagesWithSize.sort(
(a, b) => (b.imageSize.height * b.imageSize.width).compareTo(
a.imageSize.height * a.imageSize.width,
),
);
}
Problem:
This does not work with this link:
HM Productlink
I get this URL:
//lp2.hm.com/hmgoepprod?set=quality%5B79%5D%2Csource%5B%2F0c%2Fe6%2F0ce67f87aa6691557f30371590cf854ed0fb77c7.jpg%5D%2Corigin%5Bdam%5D%2Ccategory%5B%5D%2Ctype%5BLOOKBOOK%5D%2Cres%5Bm%5D%2Chmver%5B1%5D&call=url[file:/product/main]
And this is not a valid URL...
How can I parse the image from this website?
Let me know if you need any more info!
Links with leading double slashes are valid in HTML, as part of RFC1808. They will be replaced by http or https depending on the context. It will probably work if you add the scheme (http: or https:) from _currentUrl to imageSourceUrl.
I have not tested this, but I assume something like this would work:
if (!imageSourceUrl.hasScheme) {
final scheme = Uri.parse(_currentUrl).scheme;
imageSourceUrl = imageSourceUrl.replace(scheme: scheme);
}

How to upload large files in flutter web?

There is already a great thread about how to pick files:
Article
I end up with List<PlatformFile>? files if I use file_picker.
I passed withReadStream: true to the pickFiles method so I get a stream.
Here is my code so far:
List<PlatformFile>? files = fileUploadView.result?.files;
for (PlatformFile file in files!) {
//-----add selected file with request
request.files.add(http.MultipartFile(
"Your parameter name on server side", file.readStream!,
file.size,
filename: file.name));
}
//-------Send request
var resp = await request.send();
But if I run it an error occours every few seconds:
RangeError: Array buffer allocation failed
at new ArrayBuffer (<anonymous>)
at new Uint8Array (<anonymous>)
at Function._create1 (http://localhost:54372/dart_sdk.js:32192:14)
at Function.new (http://localhost:54372/dart_sdk.js:32155:49)
A few weeks later I am able to post an answer: The way to go is to use a chunk uploader.
This means to manually send the file in little parts. I send 99MB per request for example.
There is already a basic implementation of this online:
https://pub.dev/packages/chunked_uploader
You have to get a stream, this is possible with the file_picker or the drop_zone library. I used the drop_zone library because it provides the file picker and the drop zone functionality. In my code the dynamic file objects come from the drop_zone library.
Maybe you have to adjust the chunk uploader functionality depending one your backend. I use a django backend where I wrote a simple view that saves the files. In case of small files it can receive multipart requests with multiple files, in case of large files it can receive chunks and continiues to write a file if a previous chunk was received.
Here some parts of my code:
Python backend:
#api_view(["POST"])
def upload(request):
basePath = config.get("BasePath")
targetFolder = os.path.join(basePath, request.data["taskId"], "input")
if not os.path.exists(targetFolder):
os.makedirs(targetFolder)
for count, file in enumerate(request.FILES.getlist("Your parameter name on server side")):
path = os.path.join(targetFolder, file.name)
print(path)
with open(path, 'ab') as destination:
for chunk in file.chunks():
destination.write(chunk)
return HttpResponse("File(s) uploaded!")
flutter chunk uploader in my version:
import 'dart:async';
import 'dart:html';
import 'dart:math';
import 'package:dio/dio.dart';
import 'package:flutter_dropzone/flutter_dropzone.dart';
import 'package:http/http.dart' as http;
class UploadRequest {
final Dio dio;
final String url;
final String method;
final String fileKey;
final Map<String, String>? bodyData;
final Map<String, String>? headers;
final CancelToken? cancelToken;
final dynamic file;
final Function(double)? onUploadProgress;
late final int _maxChunkSize;
int fileSize;
String fileName;
late DropzoneViewController controller;
UploadRequest(
this.dio, {
required this.url,
this.method = "POST",
this.fileKey = "file",
this.bodyData = const {},
this.cancelToken,
required this.file,
this.onUploadProgress,
int maxChunkSize = 1024 * 1024 * 99,
required this.controller,
required this.fileSize,
required this.fileName,
this.headers
}) {
_maxChunkSize = min(fileSize, maxChunkSize);
}
Future<Response?> upload() async {
Response? finalResponse;
for (int i = 0; i < _chunksCount; i++) {
final start = _getChunkStart(i);
print("start is $start");
final end = _getChunkEnd(i);
final chunkStream = _getChunkStream(start, end);
var request = http.MultipartRequest(
"POST",
Uri.parse(url),
);
//request.headers.addAll(_getHeaders(start, end));
request.headers.addAll(headers!);
//-----add other fields if needed
request.fields.addAll(bodyData!);
request.files.add(http.MultipartFile(
"Your parameter name on server side",
chunkStream,
fileSize,
filename: fileName// + i.toString(),
)
);
//-------Send request
var resp = await request.send();
//------Read response
String result = await resp.stream.bytesToString();
//-------Your response
print(result);
}
return finalResponse;
}
Stream<List<int>> _getChunkStream(int start, int end) async* {
print("reading from $start to $end");
final reader = FileReader();
final blob = file.slice(start, end);
reader.readAsArrayBuffer(blob);
await reader.onLoad.first;
yield reader.result as List<int>;
}
// Updating total upload progress
_updateProgress(int chunkIndex, int chunkCurrent, int chunkTotal) {
int totalUploadedSize = (chunkIndex * _maxChunkSize) + chunkCurrent;
double totalUploadProgress = totalUploadedSize / fileSize;
this.onUploadProgress?.call(totalUploadProgress);
}
// Returning start byte offset of current chunk
int _getChunkStart(int chunkIndex) => chunkIndex * _maxChunkSize;
// Returning end byte offset of current chunk
int _getChunkEnd(int chunkIndex) =>
min((chunkIndex + 1) * _maxChunkSize, fileSize);
// Returning a header map object containing Content-Range
// https://tools.ietf.org/html/rfc7233#section-2
Map<String, String> _getHeaders(int start, int end) {
var header = {'Content-Range': 'bytes $start-${end - 1}/$fileSize'};
if (headers != null) {
header.addAll(headers!);
}
return header;
}
// Returning chunks count based on file size and maximum chunk size
int get _chunksCount {
var result = (fileSize / _maxChunkSize).ceil();
return result;
}
}
Upload code that decides whether to upload multiple files in one request or one file divided to many requests:
//upload the large files
Map<String, String> headers = {
'Authorization': requester.loginToken!
};
fileUploadView.droppedFiles.sort((a, b) => b.size - a.size);
//calculate the sum of teh files:
double sumInMb = 0;
int divideBy = 1000000;
for (UploadableFile file in fileUploadView.droppedFiles) {
sumInMb += file.size / divideBy;
}
var dio = Dio();
int uploadedAlready = 0;
for (UploadableFile file in fileUploadView.droppedFiles) {
if (sumInMb < 99) {
break;
}
var uploadRequest = UploadRequest(
dio,
url: requester.backendApi+ "/upload",
file: file.file,
controller: fileUploadView.controller!,
fileSize: file.size,
fileName: file.name,
headers: headers,
bodyData: {
"taskId": taskId.toString(),
"user": requester.username!,
},
);
await uploadRequest.upload();
uploadedAlready++;
sumInMb -= file.size / divideBy;
}
if (uploadedAlready > 0) {
fileUploadView.droppedFiles.removeRange(0, uploadedAlready);
}
print("large files uploaded");
// upload the small files
//---Create http package multipart request object
var request = http.MultipartRequest(
"POST",
Uri.parse(requester.backendApi+ "/upload"),
);
request.headers.addAll(headers);
//-----add other fields if needed
request.fields["taskId"] = taskId.toString();
print("adding files selected with drop zone");
for (UploadableFile file in fileUploadView.droppedFiles) {
Stream<List<int>>? stream = fileUploadView.controller?.getFileStream(file.file);
print("sending " + file.name);
request.files.add(http.MultipartFile(
"Your parameter name on server side",
stream!,
file.size,
filename: file.name));
}
//-------Send request
var resp = await request.send();
//------Read response
String result = await resp.stream.bytesToString();
//-------Your response
print(result);
Hopefully this gives you a good overview how I solved the problem.

can't fetching json in flutter

It Work When String is used BUT Can't fetch String Object.
it Works :-
String? url = "https://api.thedogapi.com/v1/images/search";
var raw = await http.get(Uri.parse(url));
it is Not working :-
getInfoFromSharedPref() async {
dogApiLink = await SharedPreferenceHelper().getDogName();
}
var raw = await http.get(Uri.parse('${dogApiLink}'));
where dogApiLink is String and having Link But Not working.
bro it's direct .. I just don't understand if the return from the shared preferences that you are taking is a link ???? if it's only dog name then that's your problem.. if it's a legitimate link then
{
String uri = "The link";
var response = await http.get(Uri.parse(uri), "The headers of the api").then(){
// The task you wanna perform.
}
}

Download mp3 file at browser through API .net core

Was trying to download an mp3 file in a browser through the API that I created. But instead of receiving an mp3 file. I keep getting JSON format response. I had referred from answer in return-file-in-asp-net-core-web-api, but still, I can't download the mp3 file.
Is there any mistake that I've overlooked, please kindly help?
This is my downloading method from UI
void DownloadRecording(RecordingHistory voicehistory)
{
try
{
using (var client = new WebClient())
{
client.DownloadFile("https://2d489fd863a2.ngrok.io/api/download/" + voicehistory.RecordingId + ".mp3", voicehistory.RecordingId + ".mp3");
}
}
catch { }
}
This is my api function for downloading mp3 from server
[HttpGet("download/{recordingFile}")]
public async Task<IActionResult> DownloadVoiceRecording(string recordingFile)
{
string filePath = Directory.GetCurrentDirectory() + #"\audio\Processed\" + recordingFile;
var memory = new MemoryStream();
using (var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read))
{
await stream.CopyToAsync(memory);
}
memory.Position = 0;
var types = GetMimeTypes();
var ext = Path.GetExtension(filePath).ToLowerInvariant();
return File(filePath, types[ext], recordingFile);
}
private Dictionary<string, string> GetMimeTypes()
{
return new Dictionary<string, string>
{
{".mp3", "audio/mpeg"},
{".wav","audio/wav" }
};
}
This is the response I get from browser and Postman
{
"Version": "2.0.0.0",
"StatusCode": 200,
"Message": "Status 200 OK",
"Result":"��#� ... ... /// A lot of random symbol here
}
Because the first parameter of the return value File is a type of Stream, memory needs to be passed in.
[HttpGet("download/{recordingFile}")]
public async Task<IActionResult> DownloadVoiceRecording(string recordingFile)
{
string filePath = Directory.GetCurrentDirectory() + #"\audio\Processed\" + recordingFile;
var memory = new MemoryStream();
using (var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read))
{
await stream.CopyToAsync(memory);
}
memory.Position = 0;
var types = GetMimeTypes();
var ext = Path.GetExtension(filePath).ToLowerInvariant();
return File(memory, types[ext], recordingFile);
}
I'm using Blazor for this. It turns out that there was an API response wrapper in Blazor APIReponse middleware. I had to put my API into an exception so it won't turn into JSON when I access it. It works finally.
Below is the APIReponse wrapper in Blazor.
var formattedRequest = await FormatRequest(request);
var originalBodyStream = httpContext.Response.Body;
using (var responseBody = new MemoryStream())
{
try
{
string responseBodyContent = null;
var response = httpContext.Response;
if (new string[] { "/api/localization", "/api/data", "/api/externalauth", "/api/download" }.Any(e => request.Path.StartsWithSegments(new PathString(e.ToLower()))))
await _next.Invoke(httpContext);
else
{
response.Body = responseBody;
await _next.Invoke(httpContext);
//wrap response in ApiResponse
if (httpContext.Response.StatusCode == Status200OK)
{
responseBodyContent = await FormatResponse(response);
await HandleSuccessRequestAsync(httpContext, responseBodyContent, Status200OK);
}
else
await HandleNotSuccessRequestAsync(httpContext, httpContext.Response.StatusCode);
}