Problem sending file as FormData to GraphQL HotChocolate server using fetch-api - graphql

I am trying to send form data to a graphql server using fetch-api
<div>
<!-- HTML5 Input Form Elements -->
<h4>GraphQL API Upload Test</h4>
<input id="fileUpload" type="file" name="fileUpload" />
<input id="fileDetail" type="text" name="fileDetail" />
<button id="upload-button" onclick="uploadFile()"> Upload </button>
<!-- Ajax JavaScript File Upload Logic -->
<script>
var queryTest = `mutation Header($input: Upload!) {
testUploadMutation( file: $input) {
response
}
}`;
async function uploadFile() {
let img = {};
img.file = fileUpload.files[0];
let formData = new FormData();
formData.append("operations",JSON.stringify( { query: queryTest}));
formData.append('map', { "0": ["variables.input"] });
formData.append('0', img.file);
await fetch("/graphql/", {
method: "POST",
body: formData
}).then(response => response.json()).then(data => console.log(data));
}
</script>
</div>
but I keep getting "Invalid JSON in the map multipart field; Expected type of Dictionary<string, string[]> from the server even though the mutation works fine on Banana Cake Pop. I have been using fetch-api to run all other queries and mutation successfully.
This is only code using formData with fetch-api for file upload and its not working . Anybody know what's going on?
Mutaion on Banana Cake Pop
mutation Header($input: Upload!) {
testUploadMutation( file: $input) {
response
}
}
#variables sent using the Banana Cake Pop Variables feature
{
"input": "somefile.jpg"
}
I have also tried sending formData.append('0', img)
instead of formData.append('0', img.file) but the same error

Related

How to send by form an image in http request, and with what headers?

I searched on google and nothing...
i have a form in my view on vueJS :
<form #submit.prevent="avatar()" method="POST" enctype="multipart/form-data">
<input type="file" name="profilePicture" id="profilepicture" >
<button type="submit">Valider mon avatar?</button>
</form>
The user send an image.
My question is,
i want to send the image (sending by user with the form) in a function, and this function send the image to Http Request in headers ...
the api request begin with :
app.post('/myupload/:iduser', async (req, res) => {
try{
var { iduser } = req.params ;
const { image } = req.file ;
[...]
my function in my view on vuejs is actually :
async function avatar() {
console.log(document.getElementById("profilepicture").value);
// for having the image sending src
let response = await fetch(`http://localhost:196/myupload/${MyTokenStore.myid}`, {
method: "POST",
headers: {
"Content-Type": "multipart/form-data",
},
body: JSON.stringify(document.getElementById("profilepicture").value)
})
.then((response) => response.json())
.catch((error) => {
console.log("Failed", error)
});
if(response.error){
alert(response.message);
return
}
}
But the only parameter i make in request is the string of the src image, and the error servor is :
TypeError: Cannot destructure property 'image' of 'req.files' as it is undefined.
Please, i need help, the request run when I go directly to the url of the request (but the browser directly displays the server response):
<form :action="`http://localhost:196/myupload/${MyTokenStore.myid}`"
method="POST" enctype="multipart/form-data">
<input type="file" name="image"/>
<button type="submit">Valider mon avatar</button>
</form>
but I fail to put it in a function...
thank you.
To send an image from Vue doing a request you have to use a FormData object. For example:
/**
* #description - register user
*/
const register = async () => {
try {
const fd = new FormData();
Object.keys(user.value).forEach((key) => {
fd.append(key, user.value[key]);
});
const res = await axios.post('/register', fd, {
headers: {
'Content-Type': 'multipart/form-data',
},
});
toast.success(res.data);
} catch (err) {
console.log(err);
toast.error(err.response.data);
}
};
In the previous code, in register I'm using a FormData to send the user object to the backend. Previously, you should set it. I made it like the following example:
const setProfileImage = (e) => {
image_preview.value = URL.createObjectURL(e.target.files[0]);
document.getElementById('image_preview').style.height = '150px';
document.getElementById('image_preview').style.width = '150px';
user.value.profile_image = e.target.files[0]; // this is important for you.
user.value.image = e.target.files[0].name;
};
setProfileImage is receiving an event from an Input file. If you see the comment I write in the code, user.value.profile_image = e.target.files[0] is the entire file. The rest is to display the image and send the name to store into the database.
<div class="mb-3">
<label
for="profile_image"
class="bg-primary text-white rounded-3 text-center w-100 p-2 profile_image"
>
Seleccione su foto de perfil <i class="ps-1 bi bi-card-image"></i>
</label>
<input
type="file"
class="form-control d-none"
id="profile_image"
placeholder="Adjunte su foto de perfil"
#change="setProfileImage"
/>
</div>
This is the Input file what I was talking. The event is an #change to catch correctly the file.
Hope it works for you.

Front End File Upload using Vue and Winter Cms

I'm trying to upload images from a Vue frontend via Illuminate/Http/Request to WinterCMS.
Vue finds the file and i can console.log the File object, but I'm unsure how to get this over the api. for example I've tried
public function saveImage(Request $req){
$images = $req->files('images');
}
which doesn't work, nor does
public function saveImage(Request $req){
$images = $req['images'];
}
I'm using a controller to handle my routes eg:
Route::post('/saveImage', 'Author\Project\Controllers\ProductControl#saveImage');
I've added an attachOne relation to the plugin as usual and my form has enctype="multipart/form-data"
I've had this problem before and got around it by converting images to base64 but this project will have quite a few images and I don't want to go down that route again.
Any suggestions greatly appreciated
You can send images as regular post and use regular $request->file('images') method in your Laravel controller.
You can use Javascript FormData object. For example;
<div>
<input type="file" #change="handleImages" multiple>
<button #click="uploadImages">Upload!</button>
</div>
data: () => ({
images: []
}),
methods: {
handleImages (event) {
this.images = event.target.files
},
uploadImages () {
const formData = new FormData();
for (const i of Object.keys(this.images)) {
formData.append('images', this.images[i])
}
axios.post('/saveImage', formData, {
}).then((res) => {
console.log(res)
})
}
}

Unable to upload a file using Vue frontend and laravel server

I am trying to upload a file (File can be anything i.e. an image or pdf or doc, absolutely anything) . For that, I made a test form into my Vue component that is below
<form class="mt-5" method="post" enctype="multipart/form-data" id="uploadForm">
<div class="form-group">
<input type="file" id="test" name="test" class="form-control">
</div>
<button #click.prevent="uploadFile" type="button" class="btn btn-sm btn-primary">Upload</button>
</form>
Now when I submit form (click the upload button), I am running a function below
uploadFile() {
let something = $('#test').prop('files')[0];
console.log(something);
let form_data = new FormData();
form_data.append('file', something);
console.log(form_data);
axios.post('/upload/file', { form_data })
.then(() => {
console.log("done");
})
},
So when I console.log(something), it shows me the info of uploaded file but when I send data to server using axios and dd($request->all()) there, it shows me something => [] an empty array, that means, I am not getting the file and hence cant process it further to save it into my folders (upload).
What am I doing wrong here?
This happens because you are not setting the header Content-Type as multipart/form-data (and by default the application/json is being used) when making the request with axios, and the enctype attribute has no effect, since your not using the default form submit action. So try to pass a third argument in the post method, specifying the correct Content-Type header.
const uploadAvatar = ({ id, file }) => {
const formData = new FormData();
formData.append('avatar', file);
return axios.post(`users/${id}/avatar`, formData, {
headers: { 'Content-Type': 'multipart/form-data' },
});
};

Invalid response of GraphQL request via CORS and valid response via GraphiQL processing authentication with JWT

The problem is that I always receive a 400 response code without any payload.
Even something goes wrong graphql passes with code 200 and payload with error block. That fact, that GraphiQl works fine, tells me that settings and schema are correct. So... I'm stuck. Maybe, any ideas, where to look?
I don't think it fails because of cors itself. My app worked well under DRF. I decided to try new for me technology and overwrite app using GQL.
My Origin: http://127.0.0.1:8080
Here my Apollo Client:
const cache = new InMemoryCache();
const link = new HttpLink({
uri: "http://127.0.0.1:8000/graphql/",
credentials: "include", // cookies enabled
headers: {
"X-Csrftoken": getTokenCsrf(),
"Content-Type": "application/json"
},
fetchOptions: {
mode: "cors"
}
});
const client = new ApolloClient({
cache,
link
});
There is my request (values of vars coming from the form via the state):
const AUTH_QUERY = gql`
mutation TokenAuth($username: String!, $password: String!) {
tokenAuth(username: $username, password: $password) {
token
}
}
`;
I used react-apollo-hook for performing query:
const [sendCreds, loading, error] = useMutation(AUTH_QUERY);
const handleSubmit = e => {
sendCreds({
variables: { username: state.username, password: state.password }
});
};
my Schema (according docs):
class Mutation(graphene.ObjectType):
token_auth = graphql_jwt.ObtainJSONWebToken.Field()
verify_token = graphql_jwt.Verify.Field()
refresh_token = graphql_jwt.Refresh.Field()
schema = graphene.Schema(query=Query, mutation=Mutation)
Processing this query from GraphiQL gives me a valid response.
GraphiQL is run on http://127.0.0.1:8000/graphql on another tab of browser.
Query:
mutation TokenAuth($username: String!, $password: String!) {
tokenAuth(username: $username, password: $password) {
token
}}
Variables:
{"username": "qwerty", "password": "qwerty"}
Response:
{
"data": {
"tokenAuth": {
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6InF3ZXJ0eSIsImV4cCI6MTU2NjMwMTkwNiwib3JpZ0lhdCI6MTU2NjMwMTYwNn0.NzgecEfFcnsrudcgrlZyBCC1rutoVJFciUI-ugFq9Fg"
}}}
So, the problem was solved. Don't forget, that when you press the button without explicitly defined type, the type "submit" is used.
<form >
<div className="input-field">
<input type="text" id="username" />
<input type="password" id="password" />
<button
type="button" // that string solved my problem.
onClick={handleSubmit}
>
{msg}
</button>
</div>
</form>
When you press the submit-typed button, render is called and response tries to dispatch content to the unmounted component. But if your button has type "button", request works fine. I hope my torment will save someone his time.

ASP.NET MVC 3 - File upload through AngularJS

I'm using AngularJS with my pages, and I have a doubt: when I do post with my form, how can I pass some selected file to my ASP.NET MVC 3 Controller?
Check this out:
My form:
<form enctype="multipart/form-data" ng-controller="FilesController" ng-submit="submitingForm()">
<div>
Choose the file:
<input type="file" onchange="angular.element(this).scope().setSelectedFile(this)" />
</div>
<input type="submit" value="Confirm" />
</form>
And the AngularJS Controller:
var module = angular.module('application', []);
(function (ang, app) {
function FilesController($scope, $http) {
$scope.setSelectedFile = function (element) {
$scope.$apply(function($scope) {
$scope.selectedFile = element.files[0];
});
};
$scope.submitingForm = function() {
$http.post(url, ???????).success(function() {
// How to pass that selected file for my ASP.NET controller?
});
}
}
app.controller("FilesController", FilesController);
})(angular, module);
Thank you!!!
I am not quite familiar with AngularJS but if you are attempting to upload a file using an AJAX request you can forget about it. That's not something that could be done. You could use the HTML5 File API if you want to upload files asynchronously to the server. There's an entire section dedicated to this.
And if your client browsers do not support the HTML5 File API you could use a File Upload plugin such as FineUploader or Uploadify (there are quite many others, just google).
My Approach is to use FormData.
Create a input tag like this.
<input id="imgInp" type="file" aria-label="Add photos to your post" class="upload" name="file" onchange="angular.element(this).scope().LoadFileData(this.files)" multiple="" accept="image/*" style="margin-top: -38px; opacity: 0; width: 28px;">
And in your Angular Controller create a method LoadFileData(inputFiles) as bellow:
var formData = new FormData();
$scope.LoadFileData = function (files) {
for (var file in files) {
formData.append("file", files[file]);
}
};
Now in formData variable you will have the uploaded files. Just send it to asp.net mvc controller.
$scope.submit = function () {
$http.post("/YourController/Method", formData, {
withCredentials: true,
headers: { 'Content-Type': undefined },
transformRequest: angular.identity
}).success(function (response) {
}
In Server side, Asp.NET MVC 4 controller you will have the files
var files = Request.Files;

Resources